Backed out changeset ceff61c9fc5a (bug 889561) for frequent testfailures on pgo in /bookmarks/test_
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 29 Jun 2016 13:54:51 +0200
changeset 342963 cda8c83f6a5727cad1d971a6ccf0660aa4ea0fb7
parent 342962 3fa6d81f720a687d5b593e0f8d4eaed887724755
child 342964 3cd482f4d7fe1e0a40bb27ce3557fe10068154bd
child 343155 c38c875e31a14eb4c671b2b0884de2583ceb2901
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs889561
milestone50.0a1
backs outceff61c9fc5acc0f23ac589483d09444dea69ae7
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
Backed out changeset ceff61c9fc5a (bug 889561) for frequent testfailures on pgo in /bookmarks/test_
browser/components/places/tests/browser/browser_markPageAsFollowedLink.js
services/sync/modules/engines/bookmarks.js
services/sync/modules/engines/history.js
services/sync/tests/unit/test_bookmark_invalid.js
services/sync/tests/unit/test_history_store.js
services/sync/tps/extensions/tps/resource/modules/history.jsm
toolkit/components/places/Bookmarks.jsm
toolkit/components/places/Database.cpp
toolkit/components/places/Database.h
toolkit/components/places/FaviconHelpers.cpp
toolkit/components/places/History.cpp
toolkit/components/places/History.jsm
toolkit/components/places/PlacesDBUtils.jsm
toolkit/components/places/PlacesUtils.jsm
toolkit/components/places/SQLFunctions.cpp
toolkit/components/places/SQLFunctions.h
toolkit/components/places/UnifiedComplete.js
toolkit/components/places/nsAnnotationService.cpp
toolkit/components/places/nsNavBookmarks.cpp
toolkit/components/places/nsNavHistory.cpp
toolkit/components/places/nsNavHistoryResult.cpp
toolkit/components/places/nsPlacesAutoComplete.js
toolkit/components/places/nsPlacesExpiration.js
toolkit/components/places/nsPlacesIndexes.h
toolkit/components/places/nsPlacesTables.h
toolkit/components/places/nsTaggingService.js
toolkit/components/places/tests/PlacesTestUtils.jsm
toolkit/components/places/tests/bookmarks/test_1017502-bookmarks_foreign_count.js
toolkit/components/places/tests/browser/browser_bug248970.js
toolkit/components/places/tests/browser/browser_double_redirect.js
toolkit/components/places/tests/browser/browser_history_post.js
toolkit/components/places/tests/browser/browser_settitle.js
toolkit/components/places/tests/browser/browser_visituri.js
toolkit/components/places/tests/browser/head.js
toolkit/components/places/tests/cpp/places_test_harness.h
toolkit/components/places/tests/expiration/head_expiration.js
toolkit/components/places/tests/expiration/test_annos_expire_policy.js
toolkit/components/places/tests/expiration/test_clearHistory.js
toolkit/components/places/tests/expiration/test_outdated_analyze.js
toolkit/components/places/tests/expiration/test_pref_interval.js
toolkit/components/places/tests/expiration/test_pref_maxpages.js
toolkit/components/places/tests/expiration/xpcshell.ini
toolkit/components/places/tests/head_common.js
toolkit/components/places/tests/migration/places_v33.sqlite
toolkit/components/places/tests/migration/xpcshell.ini
toolkit/components/places/tests/queries/head_queries.js
toolkit/components/places/tests/unit/test_async_history_api.js
toolkit/components/places/tests/unit/test_history_clear.js
toolkit/components/places/tests/unit/test_hosts_triggers.js
toolkit/components/places/tests/unit/test_preventive_maintenance.js
toolkit/modules/Sqlite.jsm
--- a/browser/components/places/tests/browser/browser_markPageAsFollowedLink.js
+++ b/browser/components/places/tests/browser/browser_markPageAsFollowedLink.js
@@ -53,16 +53,15 @@ add_task(function* test() {
 
 function* getTransitionForUrl(url) {
   // Ensure all the transactions completed.
   yield PlacesTestUtils.promiseAsyncUpdates();
   let db = yield PlacesUtils.promiseDBConnection();
   let rows = yield db.execute(`
     SELECT visit_type
     FROM moz_historyvisits
-    JOIN moz_places h ON place_id = h.id
-    WHERE url_hash = hash(:url) AND url = :url`,
+    WHERE place_id = (SELECT id FROM moz_places WHERE url = :url)`,
     { url });
   if (rows.length) {
     return rows[0].getResultByName("visit_type");
   }
   return null;
 }
--- a/services/sync/modules/engines/bookmarks.js
+++ b/services/sync/modules/engines/bookmarks.js
@@ -1089,17 +1089,17 @@ BookmarksStore.prototype = {
                         .DBConnection;
     return this._stmts[query] = db.createAsyncStatement(query);
   },
 
   get _frecencyStm() {
     return this._getStmt(
         "SELECT frecency " +
         "FROM moz_places " +
-        "WHERE url_hash = hash(:url) AND url = :url " +
+        "WHERE url = :url " +
         "LIMIT 1");
   },
   _frecencyCols: ["frecency"],
 
   get _setGUIDStm() {
     return this._getStmt(
       "UPDATE moz_bookmarks " +
       "SET guid = :guid " +
--- a/services/sync/modules/engines/history.js
+++ b/services/sync/modules/engines/history.js
@@ -100,17 +100,17 @@ HistoryStore.prototype = {
                         .DBConnection;
     return this._stmts[query] = db.createAsyncStatement(query);
   },
 
   get _setGUIDStm() {
     return this._getStmt(
       "UPDATE moz_places " +
       "SET guid = :guid " +
-      "WHERE url_hash = hash(:page_url) AND url = :page_url");
+      "WHERE url = :page_url");
   },
 
   // Some helper functions to handle GUIDs
   setGUID: function setGUID(uri, guid) {
     uri = uri.spec ? uri.spec : uri;
 
     if (!guid) {
       guid = Utils.makeGUID();
@@ -122,17 +122,17 @@ HistoryStore.prototype = {
     Async.querySpinningly(stmt);
     return guid;
   },
 
   get _guidStm() {
     return this._getStmt(
       "SELECT guid " +
       "FROM moz_places " +
-      "WHERE url_hash = hash(:page_url) AND url = :page_url");
+      "WHERE url = :page_url");
   },
   _guidCols: ["guid"],
 
   GUIDForUri: function GUIDForUri(uri, create) {
     let stm = this._guidStm;
     stm.params.page_url = uri.spec ? uri.spec : uri;
 
     // Use the existing GUID if it exists
@@ -141,22 +141,22 @@ HistoryStore.prototype = {
       return result.guid;
 
     // Give the uri a GUID if it doesn't have one
     if (create)
       return this.setGUID(uri);
   },
 
   get _visitStm() {
-    return this._getStmt(`/* do not warn (bug 599936) */
-      SELECT visit_type type, visit_date date
-      FROM moz_historyvisits
-      JOIN moz_places h ON h.id = place_id
-      WHERE url_hash = hash(:url) AND url = :url
-      ORDER BY date DESC LIMIT 20`);
+    return this._getStmt(
+      "/* do not warn (bug 599936) */ " +
+      "SELECT visit_type type, visit_date date " +
+      "FROM moz_historyvisits " +
+      "WHERE place_id = (SELECT id FROM moz_places WHERE url = :url) " +
+      "ORDER BY date DESC LIMIT 20");
   },
   _visitCols: ["date", "type"],
 
   get _urlStm() {
     return this._getStmt(
       "SELECT url, title, frecency " +
       "FROM moz_places " +
       "WHERE guid = :guid");
--- a/services/sync/tests/unit/test_bookmark_invalid.js
+++ b/services/sync/tests/unit/test_bookmark_invalid.js
@@ -19,17 +19,17 @@ add_task(function* test_ignore_invalid_u
   let bmid = PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
                                                   Services.io.newURI("http://example.com/", null, null),
                                                   PlacesUtils.bookmarks.DEFAULT_INDEX,
                                                   "the title");
 
   // Now update moz_places with an invalid url.
   yield PlacesUtils.withConnectionWrapper("test_ignore_invalid_uri", Task.async(function* (db) {
     yield db.execute(
-      `UPDATE moz_places SET url = :url, url_hash = hash(:url)
+      `UPDATE moz_places SET url = :url
        WHERE id = (SELECT b.fk FROM moz_bookmarks b
        WHERE b.id = :id LIMIT 1)`,
       { id: bmid, url: "<invalid url>" });
   }));
 
   // Ensure that this doesn't throw even though the DB is now in a bad state (a
   // bookmark has an illegal url).
   engine._buildGUIDMap();
--- a/services/sync/tests/unit/test_history_store.js
+++ b/services/sync/tests/unit/test_history_store.js
@@ -184,26 +184,26 @@ add_test(function test_null_title() {
 
 add_test(function test_invalid_records() {
   _("Make sure we handle invalid URLs in places databases gracefully.");
   let connection = PlacesUtils.history
                               .QueryInterface(Ci.nsPIPlacesDatabase)
                               .DBConnection;
   let stmt = connection.createAsyncStatement(
     "INSERT INTO moz_places "
-  + "(url, url_hash, title, rev_host, visit_count, last_visit_date) "
-  + "VALUES ('invalid-uri', hash('invalid-uri'), 'Invalid URI', '.', 1, " + TIMESTAMP3 + ")"
+  + "(url, title, rev_host, visit_count, last_visit_date) "
+  + "VALUES ('invalid-uri', 'Invalid URI', '.', 1, " + TIMESTAMP3 + ")"
   );
   Async.querySpinningly(stmt);
   stmt.finalize();
   // Add the corresponding visit to retain database coherence.
   stmt = connection.createAsyncStatement(
     "INSERT INTO moz_historyvisits "
   + "(place_id, visit_date, visit_type, session) "
-  + "VALUES ((SELECT id FROM moz_places WHERE url_hash = hash('invalid-uri') AND url = 'invalid-uri'), "
+  + "VALUES ((SELECT id FROM moz_places WHERE url = 'invalid-uri'), "
   + TIMESTAMP3 + ", " + Ci.nsINavHistoryService.TRANSITION_TYPED + ", 1)"
   );
   Async.querySpinningly(stmt);
   stmt.finalize();
   do_check_attribute_count(store.getAllIDs(), 4);
 
   _("Make sure we report records with invalid URIs.");
   let invalid_uri_guid = Utils.makeGUID();
--- a/services/sync/tps/extensions/tps/resource/modules/history.jsm
+++ b/services/sync/tps/extensions/tps/resource/modules/history.jsm
@@ -65,17 +65,17 @@ var HistoryEntry = {
    */
   get _visitStm() {
     let stm = this._db.createStatement(
       "SELECT visit_type type, visit_date date " +
       "FROM moz_historyvisits " +
       "WHERE place_id = (" +
         "SELECT id " +
         "FROM moz_places " +
-        "WHERE url_hash = hash(:url) AND url = :url) " +
+        "WHERE url = :url) " +
       "ORDER BY date DESC LIMIT 20");
     this.__defineGetter__("_visitStm", () => stm);
     return stm;
   },
 
   /**
    * _getVisits
    *
--- a/toolkit/components/places/Bookmarks.jsm
+++ b/toolkit/components/places/Bookmarks.jsm
@@ -771,20 +771,24 @@ function updateBookmark(info, item, newP
     if (info.hasOwnProperty("lastModified"))
       tuples.set("lastModified", { value: PlacesUtils.toPRTime(info.lastModified) });
     if (info.hasOwnProperty("title"))
       tuples.set("title", { value: info.title });
 
     yield db.executeTransaction(function* () {
       if (info.hasOwnProperty("url")) {
         // Ensure a page exists in moz_places for this URL.
-        yield maybeInsertPlace(db, info.url);
-        // Update tuples for the update query.
+        yield db.executeCached(
+          `INSERT OR IGNORE INTO moz_places (url, rev_host, hidden, frecency, guid)
+           VALUES (:url, :rev_host, 0, :frecency, GENERATE_GUID())
+          `, { url: info.url ? info.url.href : null,
+               rev_host: PlacesUtils.getReversedHost(info.url),
+               frecency: info.url.protocol == "place:" ? 0 : -1 });
         tuples.set("url", { value: info.url.href
-                          , fragment: "fk = (SELECT id FROM moz_places WHERE url_hash = hash(:url) AND url = :url)" });
+                          , fragment: "fk = (SELECT id FROM moz_places WHERE url = :url)" });
       }
 
       if (newParent) {
         // For simplicity, update the index regardless.
         let newIndex = info.hasOwnProperty("index") ? info.index : item.index;
         tuples.set("position", { value: newIndex });
 
         if (newParent.guid == item.parentGuid) {
@@ -855,32 +859,35 @@ function insertBookmark(item, parent) {
     // If a guid was not provided, generate one, so we won't need to fetch the
     // bookmark just after having created it.
     if (!item.hasOwnProperty("guid"))
       item.guid = (yield db.executeCached("SELECT GENERATE_GUID() AS guid"))[0].getResultByName("guid");
 
     yield db.executeTransaction(function* transaction() {
       if (item.type == Bookmarks.TYPE_BOOKMARK) {
         // Ensure a page exists in moz_places for this URL.
-        // The IGNORE conflict can trigger on `guid`.
-        yield maybeInsertPlace(db, item.url);
+        yield db.executeCached(
+          `INSERT OR IGNORE INTO moz_places (url, rev_host, hidden, frecency, guid)
+           VALUES (:url, :rev_host, 0, :frecency, GENERATE_GUID())
+          `, { url: item.url.href, rev_host: PlacesUtils.getReversedHost(item.url),
+               frecency: item.url.protocol == "place:" ? 0 : -1 });
       }
 
       // Adjust indices.
       yield db.executeCached(
         `UPDATE moz_bookmarks SET position = position + 1
          WHERE parent = :parent
          AND position >= :index
         `, { parent: parent._id, index: item.index });
 
       // Insert the bookmark into the database.
       yield db.executeCached(
         `INSERT INTO moz_bookmarks (fk, type, parent, position, title,
                                     dateAdded, lastModified, guid)
-         VALUES ((SELECT id FROM moz_places WHERE url_hash = hash(:url) AND url = :url), :type, :parent,
+         VALUES ((SELECT id FROM moz_places WHERE url = :url), :type, :parent,
                  :index, :title, :date_added, :last_modified, :guid)
         `, { url: item.hasOwnProperty("url") ? item.url.href : "nonexistent",
              type: item.type, parent: parent._id, index: item.index,
              title: item.title, date_added: PlacesUtils.toPRTime(item.dateAdded),
              last_modified: PlacesUtils.toPRTime(item.lastModified), guid: item.guid });
 
       yield setAncestorsLastModified(db, item.parentGuid, item.dateAdded);
     });
@@ -1006,17 +1013,17 @@ function fetchBookmarksByURL(info) {
        SELECT b.guid, IFNULL(p.guid, "") AS parentGuid, b.position AS 'index',
               b.dateAdded, b.lastModified, b.type, b.title, h.url AS url,
               b.id AS _id, b.parent AS _parentId,
               (SELECT count(*) FROM moz_bookmarks WHERE parent = b.id) AS _childCount,
               p.parent AS _grandParentId
        FROM moz_bookmarks b
        LEFT JOIN moz_bookmarks p ON p.id = b.parent
        LEFT JOIN moz_places h ON h.id = b.fk
-       WHERE h.url_hash = hash(:url) AND h.url = :url
+       WHERE h.url = :url
        AND _grandParentId <> :tags_folder
        ORDER BY b.lastModified DESC
       `, { url: info.url.href,
            tags_folder: PlacesUtils.tagsFolderId });
 
     return rows.length ? rowsToItemsArray(rows) : null;
   }));
 }
@@ -1386,28 +1393,27 @@ function validateBookmarkObject(input, b
  * Updates frecency for a list of URLs.
  *
  * @param db
  *        the Sqlite.jsm connection handle.
  * @param urls
  *        the array of URLs to update.
  */
 var updateFrecency = Task.async(function* (db, urls) {
-  // We just use the hashes, since updating a few additional urls won't hurt.
   yield db.execute(
     `UPDATE moz_places
      SET frecency = NOTIFY_FRECENCY(
        CALCULATE_FRECENCY(id), url, guid, hidden, last_visit_date
-     ) WHERE url_hash IN ( ${urls.map(url => `hash("${url.href}")`).join(", ")} )
+     ) WHERE url IN ( ${urls.map(url => JSON.stringify(url.href)).join(", ")} )
     `);
 
   yield db.execute(
     `UPDATE moz_places
      SET hidden = 0
-     WHERE url_hash IN ( ${urls.map(url => `hash(${JSON.stringify(url.href)})`).join(", ")} )
+     WHERE url IN ( ${urls.map(url => JSON.stringify(url.href)).join(", ")} )
        AND frecency <> 0
     `);
 });
 
 /**
  * Removes any orphan annotation entries.
  *
  * @param db
@@ -1553,26 +1559,8 @@ Task.async(function* (db, folderGuids) {
                                              PlacesUtils.toPRTime(entry.lastModified),
                                              entry.type, entry._parentId,
                                              entry.guid, entry.parentGuid,
                                              "" ]);
       }
     }
   }
 });
-
-/**
- * Tries to insert a new place if it doesn't exist yet.
- * @param url
- *        A valid URL object.
- * @return {Promise} resolved when the operation is complete.
- */
-function maybeInsertPlace(db, url) {
-  // The IGNORE conflict can trigger on `guid`.
-  return db.executeCached(
-    `INSERT OR IGNORE INTO moz_places (url, url_hash, rev_host, hidden, frecency, guid)
-     VALUES (:url, hash(:url), :rev_host, 0, :frecency,
-             IFNULL((SELECT guid FROM moz_places WHERE url_hash = hash(:url) AND url = :url),
-                    GENERATE_GUID()))
-    `, { url: url.href,
-         rev_host: PlacesUtils.getReversedHost(url),
-         frecency: url.protocol == "place:" ? 0 : -1 });
-}
--- a/toolkit/components/places/Database.cpp
+++ b/toolkit/components/places/Database.cpp
@@ -842,38 +842,31 @@ Database::InitSchema(bool* aDatabaseMigr
 
       if (currentSchemaVersion < 32) {
         rv = MigrateV32Up();
         NS_ENSURE_SUCCESS(rv, rv);
       }
 
       // Firefox 49 uses schema version 32.
 
-      if (currentSchemaVersion < 33) {
-        rv = MigrateV33Up();
-        NS_ENSURE_SUCCESS(rv, rv);
-      }
-
-      // Firefox 50 uses schema version 33.
-
       // Schema Upgrades must add migration code here.
 
       rv = UpdateBookmarkRootTitles();
       // We don't want a broken localization to cause us to think
       // the database is corrupt and needs to be replaced.
       MOZ_ASSERT(NS_SUCCEEDED(rv));
     }
   }
   else {
     // This is a new database, so we have to create all the tables and indices.
 
     // moz_places.
     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_PLACES);
     NS_ENSURE_SUCCESS(rv, rv);
-    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_URL_HASH);
+    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_URL);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_FAVICON);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_REVHOST);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_VISITCOUNT);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_FRECENCY);
@@ -1041,18 +1034,16 @@ Database::InitFunctions()
   rv = GenerateGUIDFunction::create(mMainConn);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = FixupURLFunction::create(mMainConn);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = FrecencyNotificationFunction::create(mMainConn);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = StoreLastInsertedIdFunction::create(mMainConn);
   NS_ENSURE_SUCCESS(rv, rv);
-  rv = HashFunction::create(mMainConn);
-  NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
 Database::InitTempEntities()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -1775,49 +1766,16 @@ Database::MigrateV32Up() {
   nsCOMPtr<mozIStoragePendingStatement> ps;
   rv = mMainConn->ExecuteAsync(stmts, ArrayLength(stmts), nullptr,
                                getter_AddRefs(ps));
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
-nsresult
-Database::MigrateV33Up() {
-  MOZ_ASSERT(NS_IsMainThread());
-
-  nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    "DROP INDEX IF EXISTS moz_places_url_uniqueindex"
-  ));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Add an url_hash column to moz_places.
-  nsCOMPtr<mozIStorageStatement> stmt;
-  rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
-    "SELECT url_hash FROM moz_places"
-  ), getter_AddRefs(stmt));
-  if (NS_FAILED(rv)) {
-    rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-      "ALTER TABLE moz_places ADD COLUMN url_hash INTEGER DEFAULT 0 NOT NULL"
-    ));
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    "UPDATE moz_places SET url_hash = hash(url) WHERE url_hash = 0"
-  ));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Create an index on url_hash.
-  rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_URL_HASH);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
 void
 Database::Shutdown()
 {
   // As the last step in the shutdown path, finalize the database handle.
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mClosed);
 
   // Break cycles with the shutdown blockers.
@@ -1839,67 +1797,43 @@ Database::Shutdown()
     nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
       "SELECT 1 "
       "FROM moz_places "
       "WHERE guid IS NULL "
     ), getter_AddRefs(stmt));
     MOZ_ASSERT(NS_SUCCEEDED(rv));
     rv = stmt->ExecuteStep(&haveNullGuids);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
-    MOZ_ASSERT(!haveNullGuids, "Found a page without a GUID!");
+    MOZ_ASSERT(!haveNullGuids && "Found a page without a GUID!");
 
     rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
       "SELECT 1 "
       "FROM moz_bookmarks "
       "WHERE guid IS NULL "
     ), getter_AddRefs(stmt));
     MOZ_ASSERT(NS_SUCCEEDED(rv));
     rv = stmt->ExecuteStep(&haveNullGuids);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
-    MOZ_ASSERT(!haveNullGuids, "Found a bookmark without a GUID!");
+    MOZ_ASSERT(!haveNullGuids && "Found a bookmark without a GUID!");
   }
 
   { // Sanity check for unrounded dateAdded and lastModified values (bug
     // 1107308).
     bool hasUnroundedDates = false;
     nsCOMPtr<mozIStorageStatement> stmt;
 
     nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
         "SELECT 1 "
         "FROM moz_bookmarks "
         "WHERE dateAdded % 1000 > 0 OR lastModified % 1000 > 0 LIMIT 1"
       ), getter_AddRefs(stmt));
     MOZ_ASSERT(NS_SUCCEEDED(rv));
     rv = stmt->ExecuteStep(&hasUnroundedDates);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
-    MOZ_ASSERT(!hasUnroundedDates, "Found unrounded dates!");
-  }
-
-  { // Sanity check url_hash
-    bool hasNullHash = false;
-    nsCOMPtr<mozIStorageStatement> stmt;
-    nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
-      "SELECT 1 FROM moz_places WHERE url_hash = 0"
-    ), getter_AddRefs(stmt));
-    MOZ_ASSERT(NS_SUCCEEDED(rv));
-    rv = stmt->ExecuteStep(&hasNullHash);
-    MOZ_ASSERT(NS_SUCCEEDED(rv));
-    MOZ_ASSERT(!hasNullHash, "Found a place without a hash!");
-  }
-
-  { // Sanity check unique urls
-    bool hasDupeUrls = false;
-    nsCOMPtr<mozIStorageStatement> stmt;
-    nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
-      "SELECT 1 FROM moz_places GROUP BY url HAVING count(*) > 1 "
-    ), getter_AddRefs(stmt));
-    MOZ_ASSERT(NS_SUCCEEDED(rv));
-    rv = stmt->ExecuteStep(&hasDupeUrls);
-    MOZ_ASSERT(NS_SUCCEEDED(rv));
-    MOZ_ASSERT(!hasDupeUrls, "Found a duplicate url!");
+    MOZ_ASSERT(!hasUnroundedDates && "Found unrounded dates!");
   }
 #endif
 
   mMainThreadStatements.FinalizeStatements();
   mMainThreadAsyncStatements.FinalizeStatements();
 
   RefPtr< FinalizeStatementCacheProxy<mozIStorageStatement> > event =
     new FinalizeStatementCacheProxy<mozIStorageStatement>(
--- a/toolkit/components/places/Database.h
+++ b/toolkit/components/places/Database.h
@@ -13,17 +13,17 @@
 #include "mozilla/storage.h"
 #include "mozilla/storage/StatementCache.h"
 #include "mozilla/Attributes.h"
 #include "nsIEventTarget.h"
 #include "Shutdown.h"
 
 // This is the schema version. Update it at any schema change and add a
 // corresponding migrateVxx method below.
-#define DATABASE_SCHEMA_VERSION 33
+#define DATABASE_SCHEMA_VERSION 32
 
 // 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.
@@ -263,17 +263,16 @@ protected:
   nsresult MigrateV24Up();
   nsresult MigrateV25Up();
   nsresult MigrateV26Up();
   nsresult MigrateV27Up();
   nsresult MigrateV28Up();
   nsresult MigrateV30Up();
   nsresult MigrateV31Up();
   nsresult MigrateV32Up();
-  nsresult MigrateV33Up();
 
   nsresult UpdateBookmarkRootTitles();
 
   friend class ConnectionShutdownBlocker;
 
 private:
   ~Database();
 
--- a/toolkit/components/places/FaviconHelpers.cpp
+++ b/toolkit/components/places/FaviconHelpers.cpp
@@ -58,17 +58,17 @@ FetchPageInfo(const RefPtr<Database>& aD
         "LEFT JOIN moz_historyvisits parent ON parent.id = dest.from_visit "
                                           "AND dest.visit_type IN (%d, %d) "
         "LEFT JOIN moz_historyvisits grandparent ON parent.from_visit = grandparent.id "
           "AND parent.visit_type IN (%d, %d) "
         "WHERE dest.place_id = h.id "
         "AND EXISTS(SELECT 1 FROM moz_bookmarks b WHERE b.fk = r_place_id) "
         "LIMIT 1 "
       ") "
-    ") FROM moz_places h WHERE h.url_hash = hash(:page_url) AND h.url = :page_url",
+    ") FROM moz_places h WHERE h.url = :page_url",
     nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
     nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY,
     nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
     nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY
   );
 
   nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(query);
   NS_ENSURE_STATE(stmt);
@@ -243,17 +243,17 @@ FetchIconURL(const RefPtr<Database>& aDB
   MOZ_ASSERT(!NS_IsMainThread());
 
   aIconSpec.Truncate();
 
   nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(
     "SELECT f.url "
     "FROM moz_places h "
     "JOIN moz_favicons f ON h.favicon_id = f.id "
-    "WHERE h.url_hash = hash(:page_url) AND h.url = :page_url"
+    "WHERE h.url = :page_url"
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"),
                                 aPageSpec);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -691,18 +691,17 @@ AsyncAssociateIconToPage::Run()
         "UPDATE moz_places SET favicon_id = :icon_id WHERE id = :page_id"
       );
       NS_ENSURE_STATE(stmt);
       rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), mPage.id);
       NS_ENSURE_SUCCESS(rv, rv);
     }
     else {
       stmt = DB->GetStatement(
-        "UPDATE moz_places SET favicon_id = :icon_id "
-        "WHERE url_hash = hash(:page_url) AND url = :page_url"
+        "UPDATE moz_places SET favicon_id = :icon_id WHERE url = :page_url"
       );
       NS_ENSURE_STATE(stmt);
       rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), mPage.spec);
       NS_ENSURE_SUCCESS(rv, rv);
     }
     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("icon_id"), mIcon.id);
     NS_ENSURE_SUCCESS(rv, rv);
 
--- a/toolkit/components/places/History.cpp
+++ b/toolkit/components/places/History.cpp
@@ -1952,17 +1952,17 @@ public:
       return NS_OK;
     mReadOnlyDBConn = do_QueryInterface(aConnection);
 
     // Now we can create our cached statements.
 
     if (!mIsVisitedStatement) {
       (void)mReadOnlyDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
         "SELECT 1 FROM moz_places h "
-        "WHERE url_hash = hash(?1) AND url = ?1 AND last_visit_date NOTNULL "
+        "WHERE url = ?1 AND last_visit_date NOTNULL "
       ),  getter_AddRefs(mIsVisitedStatement));
       MOZ_ASSERT(mIsVisitedStatement);
       nsresult result = mIsVisitedStatement ? NS_OK : NS_ERROR_NOT_AVAILABLE;
       for (int32_t i = 0; i < mIsVisitedCallbacks.Count(); ++i) {
         DebugOnly<nsresult> rv;
         rv = mIsVisitedCallbacks[i]->Complete(result, mIsVisitedStatement);
         MOZ_ASSERT(NS_SUCCEEDED(rv));
       }
@@ -2032,18 +2032,18 @@ nsresult
 History::InsertPlace(VisitData& aPlace)
 {
   MOZ_ASSERT(aPlace.placeId == 0, "should not have a valid place id!");
   MOZ_ASSERT(!aPlace.shouldUpdateHidden, "We should not need to update hidden");
   MOZ_ASSERT(!NS_IsMainThread(), "must be called off of the main thread!");
 
   nsCOMPtr<mozIStorageStatement> stmt = GetStatement(
       "INSERT INTO moz_places "
-        "(url, url_hash, title, rev_host, hidden, typed, frecency, guid) "
-      "VALUES (:url, hash(:url), :title, :rev_host, :hidden, :typed, :frecency, :guid) "
+        "(url, title, rev_host, hidden, typed, frecency, guid) "
+      "VALUES (:url, :title, :rev_host, :hidden, :typed, :frecency, :guid) "
     );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = stmt->BindStringByName(NS_LITERAL_CSTRING("rev_host"),
                                        aPlace.revHost);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("url"), aPlace.spec);
@@ -2142,17 +2142,17 @@ History::FetchPageInfo(VisitData& _place
   nsCOMPtr<mozIStorageStatement> stmt;
   bool selectByURI = !_place.spec.IsEmpty();
   if (selectByURI) {
     stmt = GetStatement(
       "SELECT guid, id, title, hidden, typed, frecency, visit_count, last_visit_date, "
       "(SELECT id FROM moz_historyvisits "
        "WHERE place_id = h.id AND visit_date = h.last_visit_date) AS last_visit_id "
       "FROM moz_places h "
-      "WHERE url_hash = hash(:page_url) AND url = :page_url "
+      "WHERE url = :page_url "
     );
     NS_ENSURE_STATE(stmt);
 
     rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), _place.spec);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   else {
     stmt = GetStatement(
--- a/toolkit/components/places/History.jsm
+++ b/toolkit/components/places/History.jsm
@@ -633,18 +633,17 @@ var clear = Task.async(function* (db) {
   let observers = PlacesUtils.history.getObservers();
   notify(observers, "onClearHistory");
 
   // Invalidate frecencies for the remaining places. This must happen
   // after the notification to ensure it runs enqueued to expiration.
   yield db.execute(
     `UPDATE moz_places SET frecency =
      (CASE
-      WHEN url_hash BETWEEN hash("place", "prefix_lo") AND
-                            hash("place", "prefix_hi")
+      WHEN url BETWEEN 'place:' AND 'place;'
       THEN 0
       ELSE -1
       END)
      WHERE frecency > 0`);
 
   // Notify frecency change observers.
   notify(observers, "onManyFrecenciesChanged");
 });
@@ -858,22 +857,20 @@ var removeVisitsByFilter = Task.async(fu
   return visitsToRemove.length != 0;
 });
 
 
 // Inner implementation of History.remove.
 var remove = Task.async(function*(db, {guids, urls}, onResult = null) {
   // 1. Find out what needs to be removed
   let query =
-    `SELECT id, url, guid, foreign_count, title, frecency
-     FROM moz_places
+    `SELECT id, url, guid, foreign_count, title, frecency FROM moz_places
      WHERE guid IN (${ sqlList(guids) })
-        OR (url_hash IN (${ urls.map(u => "hash(" + JSON.stringify(u) + ")").join(",") })
-            AND url IN (${ sqlList(urls) }))
-    `;
+        OR url  IN (${ sqlList(urls)  })
+     `;
 
   let onResultData = onResult ? [] : null;
   let pages = [];
   let hasPagesToKeep = false;
   let hasPagesToRemove = false;
   yield db.execute(query, null, Task.async(function*(row) {
     let hasForeign = row.getResultByName("foreign_count") != 0;
     if (hasForeign) {
--- a/toolkit/components/places/PlacesDBUtils.jsm
+++ b/toolkit/components/places/PlacesDBUtils.jsm
@@ -713,25 +713,19 @@ this.PlacesDBUtils = {
          LEFT JOIN moz_bookmarks on fk = h.id AND fk ISNULL
          GROUP BY src.place_id HAVING count(*) = visit_count
        )`);
     cleanupStatements.push(fixRedirectsHidden);
 
     // L.4 recalculate foreign_count.
     let fixForeignCount = DBConn.createAsyncStatement(
       `UPDATE moz_places SET foreign_count =
-         (SELECT count(*) FROM moz_bookmarks WHERE fk = moz_places.id ) +
-         (SELECT count(*) FROM moz_keywords WHERE place_id = moz_places.id )`);
+       (SELECT count(*) FROM moz_bookmarks WHERE fk = moz_places.id )`);
     cleanupStatements.push(fixForeignCount);
 
-    // L.5 recalculate missing hashes.
-    let fixMissingHashes = DBConn.createAsyncStatement(
-      `UPDATE moz_places SET url_hash = hash(url) WHERE url_hash = 0`);
-    cleanupStatements.push(fixMissingHashes);
-
     // MAINTENANCE STATEMENTS SHOULD GO ABOVE THIS POINT!
 
     return cleanupStatements;
   },
 
   /**
    * Tries to vacuum the database.
    *
--- a/toolkit/components/places/PlacesUtils.jsm
+++ b/toolkit/components/places/PlacesUtils.jsm
@@ -1299,17 +1299,17 @@ this.PlacesUtils = {
   asyncGetBookmarkIds: function PU_asyncGetBookmarkIds(aURI, aCallback)
   {
     let abort = false;
     let itemIds = [];
     Task.spawn(function* () {
       let conn = yield this.promiseDBConnection();
       const QUERY_STR = `SELECT b.id FROM moz_bookmarks b
                          JOIN moz_places h on h.id = b.fk
-                         WHERE h.url_hash = hash(:url) AND h.url = :url`;
+                         WHERE h.url = :url`;
       let spec = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
       yield conn.executeCached(QUERY_STR, { url: spec }, aRow => {
         if (abort)
           throw StopIteration;
         itemIds.push(aRow.getResultByIndex(0));
       });
       if (!abort)
         aCallback(itemIds, aURI);
@@ -2056,34 +2056,32 @@ var Keywords = {
         // A keyword can only be associated to a single page.
         // If another page is using the new keyword, we must update the keyword
         // entry.
         // Note we cannot use INSERT OR REPLACE cause it wouldn't invoke the delete
         // trigger.
         if (oldEntry) {
           yield db.executeCached(
             `UPDATE moz_keywords
-             SET place_id = (SELECT id FROM moz_places WHERE url_hash = hash(:url) AND url = :url),
+             SET place_id = (SELECT id FROM moz_places WHERE url = :url),
                  post_data = :post_data
              WHERE keyword = :keyword
             `, { url: url.href, keyword: keyword, post_data: postData });
           yield notifyKeywordChange(oldEntry.url.href, "");
         } else {
           // An entry for the given page could be missing, in such a case we need to
-          // create it.  The IGNORE conflict can trigger on `guid`.
+          // create it.
           yield db.executeCached(
-            `INSERT OR IGNORE INTO moz_places (url, url_hash, rev_host, hidden, frecency, guid)
-             VALUES (:url, hash(:url), :rev_host, 0, :frecency,
-                     IFNULL((SELECT guid FROM moz_places WHERE url_hash = hash(:url) AND url = :url),
-                            GENERATE_GUID()))
+            `INSERT OR IGNORE INTO moz_places (url, rev_host, hidden, frecency, guid)
+             VALUES (:url, :rev_host, 0, :frecency, GENERATE_GUID())
             `, { url: url.href, rev_host: PlacesUtils.getReversedHost(url),
                  frecency: url.protocol == "place:" ? 0 : -1 });
           yield db.executeCached(
             `INSERT INTO moz_keywords (keyword, place_id, post_data)
-             VALUES (:keyword, (SELECT id FROM moz_places WHERE url_hash = hash(:url) AND url = :url), :post_data)
+             VALUES (:keyword, (SELECT id FROM moz_places WHERE url = :url), :post_data)
             `, { url: url.href, keyword: keyword, post_data: postData });
         }
 
         cache.set(keyword, { keyword, url, postData });
 
         // In any case, notify about the new keyword.
         yield notifyKeywordChange(url.href, keyword);
       }.bind(this))
--- a/toolkit/components/places/SQLFunctions.cpp
+++ b/toolkit/components/places/SQLFunctions.cpp
@@ -12,17 +12,16 @@
 #include "SQLFunctions.h"
 #include "nsMathUtils.h"
 #include "nsUTF8Utils.h"
 #include "nsINavHistoryService.h"
 #include "nsPrintfCString.h"
 #include "nsNavHistory.h"
 #include "mozilla/Likely.h"
 #include "nsVariant.h"
-#include "mozilla/HashFunctions.h"
 
 // Maximum number of chars to search through.
 // MatchAutoCompleteFunction won't look for matches over this threshold.
 #define MAX_CHARS_TO_SEARCH_THROUGH 255
 
 using namespace mozilla::storage;
 
 // Keep the GUID-related parts of this file in sync with toolkit/downloads/SQLFunctions.cpp!
@@ -179,16 +178,23 @@ namespace {
 } // End anonymous namespace
 
 namespace mozilla {
 namespace places {
 
 ////////////////////////////////////////////////////////////////////////////////
 //// AutoComplete Matching Function
 
+  //////////////////////////////////////////////////////////////////////////////
+  //// MatchAutoCompleteFunction
+
+  MatchAutoCompleteFunction::~MatchAutoCompleteFunction()
+  {
+  }
+
   /* static */
   nsresult
   MatchAutoCompleteFunction::create(mozIStorageConnection *aDBConn)
   {
     RefPtr<MatchAutoCompleteFunction> function =
       new MatchAutoCompleteFunction();
 
     nsresult rv = aDBConn->CreateFunction(
@@ -317,16 +323,19 @@ namespace places {
     };
   }
 
   NS_IMPL_ISUPPORTS(
     MatchAutoCompleteFunction,
     mozIStorageFunction
   )
 
+  //////////////////////////////////////////////////////////////////////////////
+  //// mozIStorageFunction
+
   NS_IMETHODIMP
   MatchAutoCompleteFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
                                             nsIVariant **_result)
   {
     // Macro to make the code a bit cleaner and easier to read.  Operates on
     // searchBehavior.
     int32_t searchBehavior = aArguments->AsInt32(kArgIndexSearchBehavior);
     #define HAS_BEHAVIOR(aBitName) \
@@ -424,16 +433,23 @@ namespace places {
     return NS_OK;
     #undef HAS_BEHAVIOR
   }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Frecency Calculation Function
 
+  //////////////////////////////////////////////////////////////////////////////
+  //// CalculateFrecencyFunction
+
+  CalculateFrecencyFunction::~CalculateFrecencyFunction()
+  {
+  }
+
   /* static */
   nsresult
   CalculateFrecencyFunction::create(mozIStorageConnection *aDBConn)
   {
     RefPtr<CalculateFrecencyFunction> function =
       new CalculateFrecencyFunction();
 
     nsresult rv = aDBConn->CreateFunction(
@@ -444,16 +460,19 @@ namespace places {
     return NS_OK;
   }
 
   NS_IMPL_ISUPPORTS(
     CalculateFrecencyFunction,
     mozIStorageFunction
   )
 
+  //////////////////////////////////////////////////////////////////////////////
+  //// mozIStorageFunction
+
   NS_IMETHODIMP
   CalculateFrecencyFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
                                             nsIVariant **_result)
   {
     // Fetch arguments.  Use default values if they were omitted.
     uint32_t numEntries;
     nsresult rv = aArguments->GetNumEntries(&numEntries);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -604,16 +623,23 @@ namespace places {
     NS_ADDREF(*_result = new IntegerVariant((int32_t) ceilf(visitCount * ceilf(pointsForSampledVisits))));
 
     return NS_OK;
   }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// GUID Creation Function
 
+  GenerateGUIDFunction::~GenerateGUIDFunction()
+  {
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// GenerateGUIDFunction
+
   /* static */
   nsresult
   GenerateGUIDFunction::create(mozIStorageConnection *aDBConn)
   {
     RefPtr<GenerateGUIDFunction> function = new GenerateGUIDFunction();
     nsresult rv = aDBConn->CreateFunction(
       NS_LITERAL_CSTRING("generate_guid"), 0, function
     );
@@ -622,31 +648,41 @@ namespace places {
     return NS_OK;
   }
 
   NS_IMPL_ISUPPORTS(
     GenerateGUIDFunction,
     mozIStorageFunction
   )
 
+  //////////////////////////////////////////////////////////////////////////////
+  //// mozIStorageFunction
+
   NS_IMETHODIMP
   GenerateGUIDFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
                                        nsIVariant **_result)
   {
     nsAutoCString guid;
     nsresult rv = GenerateGUID(guid);
     NS_ENSURE_SUCCESS(rv, rv);
 
     NS_ADDREF(*_result = new UTF8TextVariant(guid));
     return NS_OK;
   }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Get Unreversed Host Function
 
+  GetUnreversedHostFunction::~GetUnreversedHostFunction()
+  {
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// GetUnreversedHostFunction
+
   /* static */
   nsresult
   GetUnreversedHostFunction::create(mozIStorageConnection *aDBConn)
   {
     RefPtr<GetUnreversedHostFunction> function = new GetUnreversedHostFunction();
     nsresult rv = aDBConn->CreateFunction(
       NS_LITERAL_CSTRING("get_unreversed_host"), 1, function
     );
@@ -655,16 +691,19 @@ namespace places {
     return NS_OK;
   }
 
   NS_IMPL_ISUPPORTS(
     GetUnreversedHostFunction,
     mozIStorageFunction
   )
 
+  //////////////////////////////////////////////////////////////////////////////
+  //// mozIStorageFunction
+
   NS_IMETHODIMP
   GetUnreversedHostFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
                                             nsIVariant **_result)
   {
     // Must have non-null function arguments.
     MOZ_ASSERT(aArguments);
 
     nsAutoString src;
@@ -683,16 +722,23 @@ namespace places {
     }
     result.forget(_result);
     return NS_OK;
   }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Fixup URL Function
 
+  FixupURLFunction::~FixupURLFunction()
+  {
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// FixupURLFunction
+
   /* static */
   nsresult
   FixupURLFunction::create(mozIStorageConnection *aDBConn)
   {
     RefPtr<FixupURLFunction> function = new FixupURLFunction();
     nsresult rv = aDBConn->CreateFunction(
       NS_LITERAL_CSTRING("fixup_url"), 1, function
     );
@@ -701,16 +747,19 @@ namespace places {
     return NS_OK;
   }
 
   NS_IMPL_ISUPPORTS(
     FixupURLFunction,
     mozIStorageFunction
   )
 
+  //////////////////////////////////////////////////////////////////////////////
+  //// mozIStorageFunction
+
   NS_IMETHODIMP
   FixupURLFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
                                    nsIVariant **_result)
   {
     // Must have non-null function arguments.
     MOZ_ASSERT(aArguments);
 
     nsAutoString src;
@@ -733,16 +782,20 @@ namespace places {
     result->SetAsAString(src);
     result.forget(_result);
     return NS_OK;
   }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Frecency Changed Notification Function
 
+  FrecencyNotificationFunction::~FrecencyNotificationFunction()
+  {
+  }
+
   /* static */
   nsresult
   FrecencyNotificationFunction::create(mozIStorageConnection *aDBConn)
   {
     RefPtr<FrecencyNotificationFunction> function =
       new FrecencyNotificationFunction();
     nsresult rv = aDBConn->CreateFunction(
       NS_LITERAL_CSTRING("notify_frecency"), 5, function
@@ -789,16 +842,20 @@ namespace places {
     NS_ENSURE_SUCCESS(rv, rv);
     result.forget(_result);
     return NS_OK;
   }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Store Last Inserted Id Function
 
+  StoreLastInsertedIdFunction::~StoreLastInsertedIdFunction()
+  {
+  }
+
   /* static */
   nsresult
   StoreLastInsertedIdFunction::create(mozIStorageConnection *aDBConn)
   {
     RefPtr<StoreLastInsertedIdFunction> function =
       new StoreLastInsertedIdFunction();
     nsresult rv = aDBConn->CreateFunction(
       NS_LITERAL_CSTRING("store_last_inserted_id"), 2, function
@@ -832,88 +889,10 @@ namespace places {
 
     RefPtr<nsVariant> result = new nsVariant();
     rv = result->SetAsInt64(lastInsertedId);
     NS_ENSURE_SUCCESS(rv, rv);
     result.forget(_result);
     return NS_OK;
   }
 
-////////////////////////////////////////////////////////////////////////////////
-//// Hash Function
-
-  /* static */
-  nsresult
-  HashFunction::create(mozIStorageConnection *aDBConn)
-  {
-    RefPtr<HashFunction> function = new HashFunction();
-    return aDBConn->CreateFunction(
-      NS_LITERAL_CSTRING("hash"), -1, function
-    );
-  }
-
-  NS_IMPL_ISUPPORTS(
-    HashFunction,
-    mozIStorageFunction
-  )
-
-  NS_IMETHODIMP
-  HashFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
-                               nsIVariant **_result)
-  {
-    // Must have non-null function arguments.
-    MOZ_ASSERT(aArguments);
-
-    // Fetch arguments.  Use default values if they were omitted.
-    uint32_t numEntries;
-    nsresult rv = aArguments->GetNumEntries(&numEntries);
-    NS_ENSURE_SUCCESS(rv, rv);
-    NS_ENSURE_TRUE(numEntries >= 1  && numEntries <= 2, NS_ERROR_FAILURE);
-
-    nsString str;
-    aArguments->GetString(0, str);
-    nsAutoCString mode;
-    if (numEntries > 1) {
-      aArguments->GetUTF8String(1, mode);
-    }
-
-    RefPtr<nsVariant> result = new nsVariant();
-    if (mode.IsEmpty()) {
-      // URI-like strings (having a prefix before a colon), are handled specially,
-      // as a 48 bit hash, where first 16 bits are the prefix hash, while the
-      // other 32 are the string hash.
-      // The 16 bits have been decided based on the fact hashing all of the IANA
-      // known schemes, plus "places", does not generate collisions.
-      nsAString::const_iterator start, tip, end;
-      str.BeginReading(tip);
-      start = tip;
-      str.EndReading(end);
-      if (FindInReadable(NS_LITERAL_STRING(":"), tip, end)) {
-        const nsDependentSubstring& prefix = Substring(start, tip);
-        uint64_t prefixHash = static_cast<uint64_t>(HashString(prefix) & 0x0000FFFF);
-        // The second half of the url is more likely to be unique, so we add it.
-        uint32_t srcHash = HashString(str);
-        uint64_t hash = (prefixHash << 32) + srcHash;
-        result->SetAsInt64(hash);
-      } else {
-        uint32_t hash = HashString(str);
-        result->SetAsInt64(hash);
-      }
-    } else if (mode.Equals(NS_LITERAL_CSTRING("prefix_lo"))) {
-      // Keep only 16 bits.
-      uint64_t hash = static_cast<uint64_t>(HashString(str) & 0x0000FFFF) << 32;
-      result->SetAsInt64(hash);
-    } else if (mode.Equals(NS_LITERAL_CSTRING("prefix_hi"))) {
-      // Keep only 16 bits.
-      uint64_t hash = static_cast<uint64_t>(HashString(str) & 0x0000FFFF) << 32;
-      // Make this a prefix upper bound by filling the lowest 32 bits.
-      hash +=  0xFFFFFFFF;
-      result->SetAsInt64(hash);
-    } else {
-      return NS_ERROR_FAILURE;
-    }
-
-    result.forget(_result);
-    return NS_OK;
-  }
-
 } // namespace places
 } // namespace mozilla
--- a/toolkit/components/places/SQLFunctions.h
+++ b/toolkit/components/places/SQLFunctions.h
@@ -66,17 +66,17 @@ public:
    * Registers the function with the specified database connection.
    *
    * @param aDBConn
    *        The database connection to register with.
    */
   static nsresult create(mozIStorageConnection *aDBConn);
 
 private:
-  ~MatchAutoCompleteFunction() {}
+  ~MatchAutoCompleteFunction();
 
   /**
    * Argument Indexes
    */
   static const uint32_t kArgSearchString = 0;
   static const uint32_t kArgIndexURL = 1;
   static const uint32_t kArgIndexTitle = 2;
   static const uint32_t kArgIndexTags = 3;
@@ -169,16 +169,17 @@ private:
    * @param _fixedSpec
    *        An out parameter that is the fixed up string.
    */
   static void fixupURISpec(const nsCString &aURISpec, int32_t aMatchBehavior,
                            nsCString &_fixedSpec);
 };
 
 
+
 ////////////////////////////////////////////////////////////////////////////////
 //// Frecency Calculation Function
 
 /**
  * This function is used to calculate frecency for a page.
  *
  * In SQL, you'd use it in when setting frecency like:
  * SET frecency = CALCULATE_FRECENCY(place_id).
@@ -191,77 +192,74 @@ private:
  *        Whether the page has been typed in.  Default is false.
  * @param [optional] fullVisitCount
  *        Count of all the visits (All types).  Default is 0.
  * @param [optional] isBookmarked
  *        Whether the page is bookmarked. Default is false.
  */
 class CalculateFrecencyFunction final : public mozIStorageFunction
 {
+  ~CalculateFrecencyFunction();
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_MOZISTORAGEFUNCTION
 
   /**
    * Registers the function with the specified database connection.
    *
    * @param aDBConn
    *        The database connection to register with.
    */
   static nsresult create(mozIStorageConnection *aDBConn);
-private:
-  ~CalculateFrecencyFunction() {}
 };
 
 /**
  * SQL function to generate a GUID for a place or bookmark item.  This is just
  * a wrapper around GenerateGUID in Helpers.h.
  *
  * @return a guid for the item.
  */
 class GenerateGUIDFunction final : public mozIStorageFunction
 {
+  ~GenerateGUIDFunction();
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_MOZISTORAGEFUNCTION
 
   /**
    * Registers the function with the specified database connection.
    *
    * @param aDBConn
    *        The database connection to register with.
    */
   static nsresult create(mozIStorageConnection *aDBConn);
-private:
-  ~GenerateGUIDFunction() {}
 };
 
 /**
  * SQL function to unreverse the rev_host of a page.
  *
  * @param rev_host
  *        The rev_host value of the page.
  *
  * @return the unreversed host of the page.
  */
 class GetUnreversedHostFunction final : public mozIStorageFunction
 {
+  ~GetUnreversedHostFunction();
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_MOZISTORAGEFUNCTION
 
   /**
    * Registers the function with the specified database connection.
    *
    * @param aDBConn
    *        The database connection to register with.
    */
   static nsresult create(mozIStorageConnection *aDBConn);
-private:
-  ~GetUnreversedHostFunction() {}
 };
 
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Fixup URL Function
 
 /**
  * Make a given URL more suitable for searches, by removing common prefixes
@@ -269,29 +267,28 @@ private:
  *
  * @param url
  *        A URL.
  * @return
  *        The same URL, with redundant parts removed.
  */
 class FixupURLFunction final : public mozIStorageFunction
 {
+  ~FixupURLFunction();
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_MOZISTORAGEFUNCTION
 
   /**
    * Registers the function with the specified database connection.
    *
    * @param aDBConn
    *        The database connection to register with.
    */
   static nsresult create(mozIStorageConnection *aDBConn);
-private:
-  ~FixupURLFunction() {}
 };
 
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Frecency Changed Notification Function
 
 /**
  * For a given place, posts a runnable to the main thread that calls
@@ -307,29 +304,28 @@ private:
  * @param hidden
  *        The place's hidden boolean.
  * @param lastVisitDate
  *        The place's last visit date.
  * @return newFrecency
  */
 class FrecencyNotificationFunction final : public mozIStorageFunction
 {
+  ~FrecencyNotificationFunction();
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_MOZISTORAGEFUNCTION
 
   /**
    * Registers the function with the specified database connection.
    *
    * @param aDBConn
    *        The database connection to register with.
    */
   static nsresult create(mozIStorageConnection *aDBConn);
-private:
-  ~FrecencyNotificationFunction() {}
 };
 
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Store Last Inserted Id Function
 
 /**
  * Store the last inserted id for reference purpose.
@@ -337,56 +333,26 @@ private:
  * @param tableName
  *        The table name.
  * @param id
  *        The last inserted id.
  * @return null
  */
 class StoreLastInsertedIdFunction final : public mozIStorageFunction
 {
+  ~StoreLastInsertedIdFunction();
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_MOZISTORAGEFUNCTION
 
   /**
    * Registers the function with the specified database connection.
    *
    * @param aDBConn
    *        The database connection to register with.
    */
   static nsresult create(mozIStorageConnection *aDBConn);
-private:
-  ~StoreLastInsertedIdFunction() {}
-};
-
-
-////////////////////////////////////////////////////////////////////////////////
-//// Hash Function
-
-/**
- * Calculates hash for a given string using the mfbt AddToHash function.
- *
- * @param string
- *        A string.
- * @return
- *        The hash for the string.
- */
-class HashFunction final : public mozIStorageFunction
-{
-public:
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_MOZISTORAGEFUNCTION
-
-  /**
-   * Registers the function with the specified database connection.
-   *
-   * @param aDBConn
-   *        The database connection to register with.
-   */
-  static nsresult create(mozIStorageConnection *aDBConn);
-private:
-  ~HashFunction() {}
 };
 
 } // namespace places
 } // namespace mozilla
 
 #endif // mozilla_places_SQLFunctions_h_
--- a/toolkit/components/places/UnifiedComplete.js
+++ b/toolkit/components/places/UnifiedComplete.js
@@ -127,17 +127,17 @@ function defaultQuery(conditions = "") {
      LIMIT :maxResults`;
   return query;
 }
 
 const SQL_SWITCHTAB_QUERY =
   `SELECT :query_type, t.url, t.url, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
           t.open_count, NULL
    FROM moz_openpages_temp t
-   LEFT JOIN moz_places h ON h.url_hash = hash(t.url) AND h.url = t.url
+   LEFT JOIN moz_places h ON h.url = t.url
    WHERE h.id IS NULL
      AND AUTOCOMPLETE_MATCH(:searchString, t.url, t.url, NULL,
                             NULL, NULL, NULL, t.open_count,
                             :matchBehavior, :searchBehavior)
    ORDER BY t.ROWID DESC
    LIMIT :maxResults`;
 
 const SQL_ADAPTIVE_QUERY =
--- a/toolkit/components/places/nsAnnotationService.cpp
+++ b/toolkit/components/places/nsAnnotationService.cpp
@@ -1255,17 +1255,17 @@ nsAnnotationService::GetAnnotationNamesT
     );
   }
   else {
     statement = mDB->GetStatement(
       "SELECT n.name "
       "FROM moz_anno_attributes n "
       "JOIN moz_annos a ON a.anno_attribute_id = n.id "
       "JOIN moz_places h ON h.id = a.place_id "
-      "WHERE h.url_hash = hash(:page_url) AND h.url = :page_url"
+      "WHERE h.url = :page_url"
     );
   }
   NS_ENSURE_STATE(statement);
   mozStorageStatementScoper scoper(statement);
 
   nsresult rv;
   if (isItemAnnotation)
     rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId);
@@ -1377,18 +1377,17 @@ nsAnnotationService::RemoveAnnotationInt
       "WHERE item_id = :item_id "
         "AND anno_attribute_id = "
           "(SELECT id FROM moz_anno_attributes WHERE name = :anno_name)"
     );
   }
   else {
     statement = mDB->GetStatement(
       "DELETE FROM moz_annos "
-      "WHERE place_id = "
-          "(SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url) "
+      "WHERE place_id = (SELECT id FROM moz_places WHERE url = :page_url) "
         "AND anno_attribute_id = "
           "(SELECT id FROM moz_anno_attributes WHERE name = :anno_name)"
     );
   }
   NS_ENSURE_STATE(statement);
   mozStorageStatementScoper scoper(statement);
 
   nsresult rv;
@@ -1441,17 +1440,17 @@ nsAnnotationService::RemoveItemAnnotatio
 NS_IMETHODIMP
 nsAnnotationService::RemovePageAnnotations(nsIURI* aURI)
 {
   NS_ENSURE_ARG(aURI);
 
   // Should this be precompiled or a getter?
   nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement(
     "DELETE FROM moz_annos WHERE place_id = "
-      "(SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url)"
+      "(SELECT id FROM moz_places WHERE url = :page_url)"
   );
   NS_ENSURE_STATE(statement);
   mozStorageStatementScoper scoper(statement);
 
   nsresult rv = URIBinder::Bind(statement, NS_LITERAL_CSTRING("page_url"), aURI);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = statement->Execute();
@@ -1504,33 +1503,33 @@ nsAnnotationService::CopyPageAnnotations
   mozStorageTransaction transaction(mDB->MainConn(), false);
 
   nsCOMPtr<mozIStorageStatement> sourceStmt = mDB->GetStatement(
     "SELECT h.id, n.id, n.name, a2.id "
     "FROM moz_places h "
     "JOIN moz_annos a ON a.place_id = h.id "
     "JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id "
     "LEFT JOIN moz_annos a2 ON a2.place_id = "
-      "(SELECT id FROM moz_places WHERE url_hash = hash(:dest_url) AND url = :dest_url) "
+      "(SELECT id FROM moz_places WHERE url = :dest_url) "
                           "AND a2.anno_attribute_id = n.id "
     "WHERE url = :source_url"
   );
   NS_ENSURE_STATE(sourceStmt);
   mozStorageStatementScoper sourceScoper(sourceStmt);
 
   nsresult rv = URIBinder::Bind(sourceStmt, NS_LITERAL_CSTRING("source_url"), aSourceURI);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = URIBinder::Bind(sourceStmt, NS_LITERAL_CSTRING("dest_url"), aDestURI);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<mozIStorageStatement> copyStmt = mDB->GetStatement(
     "INSERT INTO moz_annos "
     "(place_id, anno_attribute_id, content, flags, expiration, "
      "type, dateAdded, lastModified) "
-    "SELECT (SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url), "
+    "SELECT (SELECT id FROM moz_places WHERE url = :page_url), "
            "anno_attribute_id, content, flags, expiration, type, "
            ":date, :date "
     "FROM moz_annos "
     "WHERE place_id = :page_id "
     "AND anno_attribute_id = :name_id"
   );
   NS_ENSURE_STATE(copyStmt);
   mozStorageStatementScoper copyScoper(copyStmt);
@@ -1699,17 +1698,17 @@ nsAnnotationService::HasAnnotationIntern
   else {
     stmt = mDB->GetStatement(
       "SELECT h.id, "
              "(SELECT id FROM moz_anno_attributes WHERE name = :anno_name) AS nameid, "
              "a.id, a.dateAdded "
       "FROM moz_places h "
       "LEFT JOIN moz_annos a ON a.place_id = h.id "
                            "AND a.anno_attribute_id = nameid "
-      "WHERE h.url_hash = hash(:page_url) AND h.url = :page_url"
+      "WHERE h.url = :page_url"
     );
   }
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper checkAnnoScoper(stmt);
 
   nsresult rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"), aName);
   NS_ENSURE_SUCCESS(rv, rv);
   if (isItemAnnotation)
@@ -1766,17 +1765,17 @@ nsAnnotationService::StartGetAnnotation(
   }
   else {
     aStatement = mDB->GetStatement(
       "SELECT a.id, a.place_id, :anno_name, a.content, a.flags, "
              "a.expiration, a.type "
       "FROM moz_anno_attributes n "
       "JOIN moz_annos a ON n.id = a.anno_attribute_id "
       "JOIN moz_places h ON h.id = a.place_id "
-      "WHERE h.url_hash = hash(:page_url) AND h.url = :page_url "
+      "WHERE h.url = :page_url "
         "AND n.name = :anno_name"
     );
   }
   NS_ENSURE_STATE(aStatement);
   mozStorageStatementScoper getAnnoScoper(aStatement);
 
   nsresult rv;
   if (isItemAnnotation)
@@ -1860,17 +1859,17 @@ nsAnnotationService::StartSetAnnotation(
   else {
     stmt = mDB->GetStatement(
       "SELECT h.id, "
              "(SELECT id FROM moz_anno_attributes WHERE name = :anno_name) AS nameid, "
              "a.id, a.dateAdded "
       "FROM moz_places h "
       "LEFT JOIN moz_annos a ON a.place_id = h.id "
                            "AND a.anno_attribute_id = nameid "
-      "WHERE h.url_hash = hash(:page_url) AND h.url = :page_url"
+      "WHERE h.url = :page_url"
     );
   }
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper checkAnnoScoper(stmt);
 
   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"), aName);
   NS_ENSURE_SUCCESS(rv, rv);
   if (isItemAnnotation)
--- a/toolkit/components/places/nsNavBookmarks.cpp
+++ b/toolkit/components/places/nsNavBookmarks.cpp
@@ -55,17 +55,17 @@ public:
   {
     RefPtr<Database> DB = Database::GetDatabase();
     if (DB) {
       nsCOMPtr<mozIStorageAsyncStatement> stmt = DB->GetAsyncStatement(
         "/* do not warn (bug 1175249) */ "
         "SELECT b.id, b.guid, b.parent, b.lastModified, t.guid, t.parent "
         "FROM moz_bookmarks b "
         "JOIN moz_bookmarks t on t.id = b.parent "
-        "WHERE b.fk = (SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url) "
+        "WHERE b.fk = (SELECT id FROM moz_places WHERE url = :page_url) "
         "ORDER BY b.lastModified DESC, b.id DESC "
       );
       if (stmt) {
         (void)URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"),
                               mData.bookmark.url);
         nsCOMPtr<mozIStoragePendingStatement> pendingStmt;
         (void)stmt->ExecuteAsync(this, getter_AddRefs(pendingStmt));
       }
@@ -1848,17 +1848,17 @@ NS_IMETHODIMP
 nsNavBookmarks::IsBookmarked(nsIURI* aURI, bool* aBookmarked)
 {
   NS_ENSURE_ARG(aURI);
   NS_ENSURE_ARG_POINTER(aBookmarked);
 
   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
     "SELECT 1 FROM moz_bookmarks b "
     "JOIN moz_places h ON b.fk = h.id "
-    "WHERE h.url_hash = hash(:page_url) AND h.url = :page_url"
+    "WHERE h.url = :page_url"
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = stmt->ExecuteStep(aBookmarked);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -2054,17 +2054,17 @@ nsNavBookmarks::GetBookmarkIdsForURITArr
   // Double ordering covers possible lastModified ties, that could happen when
   // importing, syncing or due to extensions.
   // Note: not using a JOIN is cheaper in this case.
   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
     "/* do not warn (bug 1175249) */ "
     "SELECT b.id, b.guid, b.parent, b.lastModified, t.guid, t.parent "
     "FROM moz_bookmarks b "
     "JOIN moz_bookmarks t on t.id = b.parent "
-    "WHERE b.fk = (SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url) "
+    "WHERE b.fk = (SELECT id FROM moz_places WHERE url = :page_url) "
     "ORDER BY b.lastModified DESC, b.id DESC "
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -2098,17 +2098,17 @@ nsNavBookmarks::GetBookmarksForURI(nsIUR
   // Double ordering covers possible lastModified ties, that could happen when
   // importing, syncing or due to extensions.
   // Note: not using a JOIN is cheaper in this case.
   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
     "/* do not warn (bug 1175249) */ "
     "SELECT b.id, b.guid, b.parent, b.lastModified, t.guid, t.parent "
     "FROM moz_bookmarks b "
     "JOIN moz_bookmarks t on t.id = b.parent "
-    "WHERE b.fk = (SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url) "
+    "WHERE b.fk = (SELECT id FROM moz_places WHERE url = :page_url) "
     "ORDER BY b.lastModified DESC, b.id DESC "
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
   NS_ENSURE_SUCCESS(rv, rv);
 
--- a/toolkit/components/places/nsNavHistory.cpp
+++ b/toolkit/components/places/nsNavHistory.cpp
@@ -364,17 +364,17 @@ nsNavHistory::GetIdForPage(nsIURI* aURI,
                            int64_t* _pageId,
                            nsCString& _GUID)
 {
   *_pageId = 0;
 
   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
     "SELECT id, url, title, rev_host, visit_count, guid "
     "FROM moz_places "
-    "WHERE url_hash = hash(:page_url) AND url = :page_url "
+    "WHERE url = :page_url "
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool hasEntry = false;
@@ -400,18 +400,18 @@ nsNavHistory::GetOrCreateIdForPage(nsIUR
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (*_pageId != 0) {
     return NS_OK;
   }
 
   // Create a new hidden, untyped and unvisited entry.
   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
-    "INSERT INTO moz_places (url, url_hash, rev_host, hidden, frecency, guid) "
-    "VALUES (:page_url, hash(:page_url), :rev_host, :hidden, :frecency, :guid) "
+    "INSERT INTO moz_places (url, rev_host, hidden, frecency, guid) "
+    "VALUES (:page_url, :rev_host, :hidden, :frecency, :guid) "
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
   NS_ENSURE_SUCCESS(rv, rv);
   // host (reversed with trailing period)
   nsAutoString revHost;
@@ -1024,18 +1024,17 @@ nsNavHistory::invalidateFrecencies(const
   // Exclude place: queries by setting their frecency to zero.
   nsCString invalidFrecenciesSQLFragment(
     "UPDATE moz_places SET frecency = "
   );
   if (!aPlaceIdsQueryString.IsEmpty())
     invalidFrecenciesSQLFragment.AppendLiteral("NOTIFY_FRECENCY(");
   invalidFrecenciesSQLFragment.AppendLiteral(
       "(CASE "
-       "WHEN url_hash BETWEEN hash('place', 'prefix_lo') AND "
-                             "hash('place', 'prefix_hi') "
+       "WHEN url BETWEEN 'place:' AND 'place;' "
        "THEN 0 "
        "ELSE -1 "
        "END) "
   );
   if (!aPlaceIdsQueryString.IsEmpty()) {
     invalidFrecenciesSQLFragment.AppendLiteral(
       ", url, guid, hidden, last_visit_date) "
     );
@@ -1829,18 +1828,17 @@ PlacesSQLQueryBuilder::SelectAsSite()
     "SELECT null, 'place:type=%ld&sort=%ld&domain=&domainIsHost=true'%s, "
            ":localhost, :localhost, null, null, null, null, null, null, null, "
            "null, null, null "
     "WHERE EXISTS ( "
       "SELECT h.id FROM moz_places h "
       "%s "
       "WHERE h.hidden = 0 "
         "AND h.visit_count > 0 "
-        "AND h.url_hash BETWEEN hash('file', 'prefix_lo') AND "
-                               "hash('file', 'prefix_hi') "
+        "AND h.url BETWEEN 'file://' AND 'file:/~' "
       "%s "
       "LIMIT 1 "
     ") "
     "UNION ALL "
     "SELECT null, "
            "'place:type=%ld&sort=%ld&domain='||host||'&domainIsHost=true'%s, "
            "host, host, null, null, null, null, null, null, null, "
            "null, null, null "
@@ -2898,17 +2896,17 @@ nsNavHistory::GetPageTitle(nsIURI* aURI,
   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
   NS_ENSURE_ARG(aURI);
 
   aTitle.Truncate(0);
 
   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
     "SELECT id, url, title, rev_host, visit_count, guid "
     "FROM moz_places "
-    "WHERE url_hash = hash(:page_url) AND url = :page_url "
+    "WHERE url = :page_url "
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool hasResults = false;
@@ -3444,18 +3442,17 @@ nsNavHistory::QueryToSelectClause(nsNavH
         clause.Str(",");
       }
     }
     clause.Str(")");
   }
 
   if (excludeQueries) {
     // Serching by terms implicitly exclude queries.
-    clause.Condition("NOT h.url_hash BETWEEN hash('place', 'prefix_lo') AND "
-                                            "hash('place', 'prefix_hi')");
+    clause.Condition("NOT h.url BETWEEN 'place:' AND 'place;'");
   }
 
   clause.GetClauseString(*aClause);
   return NS_OK;
 }
 
 
 // nsNavHistory::BindQueryClauseParameters
@@ -4199,17 +4196,17 @@ nsNavHistory::URIToResultNode(nsIURI* aU
     "SELECT h.id, :page_url, COALESCE(b.title, h.title), "
            "h.rev_host, h.visit_count, h.last_visit_date, f.url, "
            "b.id, b.dateAdded, b.lastModified, b.parent, "
            ) + tagsFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid, "
            "null, null, null, b.guid, b.position, b.type, b.fk "
     "FROM moz_places h "
     "LEFT JOIN moz_bookmarks b ON b.fk = h.id "
     "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
-    "WHERE h.url_hash = hash(:page_url) AND h.url = :page_url ")
+    "WHERE h.url = :page_url ")
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool hasMore = false;
@@ -4534,17 +4531,17 @@ nsNavHistory::AutoCompleteFeedback(int32
                                    nsIAutoCompleteController *aController)
 {
   nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement(
     "INSERT OR REPLACE INTO moz_inputhistory "
     // use_count will asymptotically approach the max of 10.
     "SELECT h.id, IFNULL(i.input, :input_text), IFNULL(i.use_count, 0) * .9 + 1 "
     "FROM moz_places h "
     "LEFT JOIN moz_inputhistory i ON i.place_id = h.id AND i.input = :input_text "
-    "WHERE url_hash = hash(:page_url) AND url = :page_url "
+    "WHERE url = :page_url "
   );
   NS_ENSURE_STATE(stmt);
 
   nsAutoString input;
   nsresult rv = aController->GetSearchString(input);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = stmt->BindStringByName(NS_LITERAL_CSTRING("input_text"), input);
   NS_ENSURE_SUCCESS(rv, rv);
--- a/toolkit/components/places/nsNavHistoryResult.cpp
+++ b/toolkit/components/places/nsNavHistoryResult.cpp
@@ -186,17 +186,17 @@ nsNavHistoryResultNode::GetTags(nsAStrin
   NS_ENSURE_STATE(DB);
   nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
     "/* do not warn (bug 487594) */ "
     "SELECT GROUP_CONCAT(tag_title, ', ') "
     "FROM ( "
       "SELECT t.title AS tag_title "
       "FROM moz_bookmarks b "
       "JOIN moz_bookmarks t ON t.id = +b.parent "
-      "WHERE b.fk = (SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url) "
+      "WHERE b.fk = (SELECT id FROM moz_places WHERE url = :page_url) "
         "AND t.parent = :tags_folder "
       "ORDER BY t.title COLLATE NOCASE ASC "
     ") "
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   nsNavHistory* history = nsNavHistory::GetHistoryService();
--- a/toolkit/components/places/nsPlacesAutoComplete.js
+++ b/toolkit/components/places/nsPlacesAutoComplete.js
@@ -379,17 +379,17 @@ function nsPlacesAutoComplete()
     return this._db.createAsyncStatement(baseQuery("AND tags IS NOT NULL"));
   });
 
   XPCOMUtils.defineLazyGetter(this, "_openPagesQuery", function() {
     return this._db.createAsyncStatement(
       `SELECT t.url, t.url, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
               :query_type, t.open_count, NULL
        FROM moz_openpages_temp t
-       LEFT JOIN moz_places h ON h.url_hash = hash(t.url) AND h.url = t.url
+       LEFT JOIN moz_places h ON h.url = t.url
        WHERE h.id IS NULL
          AND AUTOCOMPLETE_MATCH(:searchString, t.url, t.url, NULL,
                                 NULL, NULL, NULL, t.open_count,
                                 :matchBehavior, :searchBehavior)
        ORDER BY t.ROWID DESC
        LIMIT :maxResults`
     );
   });
--- a/toolkit/components/places/nsPlacesExpiration.js
+++ b/toolkit/components/places/nsPlacesExpiration.js
@@ -21,34 +21,31 @@
  */
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Task.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
   "resource://gre/modules/PlacesUtils.jsm");
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Constants
 
 // Last expiration step should run before the final sync.
 const TOPIC_SHUTDOWN = "places-will-close-connection";
 const TOPIC_PREF_CHANGED = "nsPref:changed";
 const TOPIC_DEBUG_START_EXPIRATION = "places-debug-start-expiration";
 const TOPIC_EXPIRATION_FINISHED = "places-expiration-finished";
 const TOPIC_IDLE_BEGIN = "idle";
 const TOPIC_IDLE_END = "active";
 const TOPIC_IDLE_DAILY = "idle-daily";
-const TOPIC_TESTING_MODE = "testing-mode";
-const TOPIC_TEST_INTERVAL_CHANGED = "test-interval-changed";
 
 // Branch for all expiration preferences.
 const PREF_BRANCH = "places.history.expiration.";
 
 // Max number of unique URIs to retain in history.
 // Notice this is a lazy limit.  This means we will start to expire if we will
 // go over it, but we won't ensure that we will stop exactly when we reach it,
 // instead we will stop after the next expiration step that will bring us
@@ -70,17 +67,17 @@ const PREF_INTERVAL_SECONDS_NOTSET = 3 *
 // This percentage of memory size is used to protect against calculating a too
 // large database size on systems with small memory.
 const DATABASE_TO_MEMORY_PERC = 4;
 // This percentage of disk size is used to protect against calculating a too
 // large database size on disks with tiny quota or available space.
 const DATABASE_TO_DISK_PERC = 2;
 // Maximum size of the optimal database.  High-end hardware has plenty of
 // memory and disk space, but performances don't grow linearly.
-const DATABASE_MAX_SIZE = 62914560; // 60MiB
+const DATABASE_MAX_SIZE = 167772160; // 160MiB
 // If the physical memory size is bogus, fallback to this.
 const MEMSIZE_FALLBACK_BYTES = 268435456; // 256 MiB
 // If the disk available space is bogus, fallback to this.
 const DISKSIZE_FALLBACK_BYTES = 268435456; // 256 MiB
 
 // Max number of entries to expire at each expiration step.
 // This value is globally used for different kind of data we expire, can be
 // tweaked based on data type.  See below in getBoundStatement.
@@ -98,18 +95,19 @@ const EXPIRE_LIMIT_PER_LARGE_STEP_MULTIP
 // 2. Dirty history:
 //   We expire at the default interval, but a greater number of entries
 //   (default number of entries * EXPIRE_AGGRESSIVITY_MULTIPLIER).
 const EXPIRE_AGGRESSIVITY_MULTIPLIER = 3;
 
 // This is the average size in bytes of an URI entry in the database.
 // Magic numbers are determined through analysis of the distribution of a ratio
 // between number of unique URIs and database size among our users.
-// Used as a fall back value when it's not possible to calculate the real value.
-const URIENTRY_AVG_SIZE = 600;
+// Based on these values we evaluate how many unique URIs we can handle before
+// starting expiring some.
+const URIENTRY_AVG_SIZE = 1600;
 
 // Seconds of idle time before starting a larger expiration step.
 // Notice during idle we stop the expiration timer since we don't want to hurt
 // stand-by or mobile devices batteries.
 const IDLE_TIMEOUT_SECONDS = 5 * 60;
 
 // If a clear history ran just before we shutdown, we will skip most of the
 // expiration at shutdown.  This is maximum number of seconds from last
@@ -479,51 +477,55 @@ function nsPlacesExpiration()
        , reason TEXT NOT NULL DEFAULT "expired"
        )`);
     stmt.executeAsync();
     stmt.finalize();
 
     return db;
   });
 
+  XPCOMUtils.defineLazyServiceGetter(this, "_sys",
+                                     "@mozilla.org/system-info;1",
+                                     "nsIPropertyBag2");
   XPCOMUtils.defineLazyServiceGetter(this, "_idle",
                                      "@mozilla.org/widget/idleservice;1",
                                      "nsIIdleService");
 
   this._prefBranch = Cc["@mozilla.org/preferences-service;1"].
                      getService(Ci.nsIPrefService).
                      getBranch(PREF_BRANCH);
+  this._loadPrefs();
 
-  this._loadPrefs().then(() => {
-    // Observe our preferences branch for changes.
-    this._prefBranch.addObserver("", this, true);
-
-    // Create our expiration timer.
-    this._newTimer();
-  }, Cu.reportError);
+  // Observe our preferences branch for changes.
+  this._prefBranch.addObserver("", this, false);
 
   // Register topic observers.
-  Services.obs.addObserver(this, TOPIC_SHUTDOWN, true);
-  Services.obs.addObserver(this, TOPIC_DEBUG_START_EXPIRATION, true);
-  Services.obs.addObserver(this, TOPIC_IDLE_DAILY, true);
+  Services.obs.addObserver(this, TOPIC_SHUTDOWN, false);
+  Services.obs.addObserver(this, TOPIC_DEBUG_START_EXPIRATION, false);
+  Services.obs.addObserver(this, TOPIC_IDLE_DAILY, false);
+
+  // Create our expiration timer.
+  this._newTimer();
 }
 
 nsPlacesExpiration.prototype = {
 
   //////////////////////////////////////////////////////////////////////////////
   //// nsIObserver
 
   observe: function PEX_observe(aSubject, aTopic, aData)
   {
-    if (this._shuttingDown) {
-      return;
-    }
-
     if (aTopic == TOPIC_SHUTDOWN) {
       this._shuttingDown = true;
+      Services.obs.removeObserver(this, TOPIC_SHUTDOWN);
+      Services.obs.removeObserver(this, TOPIC_DEBUG_START_EXPIRATION);
+      Services.obs.removeObserver(this, TOPIC_IDLE_DAILY);
+
+      this._prefBranch.removeObserver("", this);
+
       this.expireOnIdle = false;
 
       if (this._timer) {
         this._timer.cancel();
         this._timer = null;
       }
 
       // If we didn't ran a clearHistory recently and database is dirty, we
@@ -533,22 +535,22 @@ nsPlacesExpiration.prototype = {
           SHUTDOWN_WITH_RECENT_CLEARHISTORY_TIMEOUT_SECONDS * 1000;
       if (!hasRecentClearHistory && this.status == STATUS.DIRTY) {
         this._expireWithActionAndLimit(ACTION.SHUTDOWN_DIRTY, LIMIT.LARGE);
       }
 
       this._finalizeInternalStatements();
     }
     else if (aTopic == TOPIC_PREF_CHANGED) {
-      this._loadPrefs().then(() => {
-        if (aData == PREF_INTERVAL_SECONDS) {
-          // Renew the timer with the new interval value.
-          this._newTimer();
-        }
-      }, Cu.reportError);
+      this._loadPrefs();
+
+      if (aData == PREF_INTERVAL_SECONDS) {
+        // Renew the timer with the new interval value.
+        this._newTimer();
+      }
     }
     else if (aTopic == TOPIC_DEBUG_START_EXPIRATION) {
       // The passed-in limit is the maximum number of visits to expire when
       // history is over capacity.  Mind to correctly handle the NaN value.
       let limit = parseInt(aData);
       if (limit == -1) {
         // Everything should be expired without any limit.  If history is over
         // capacity then all existing visits will be expired.
@@ -583,19 +585,16 @@ nsPlacesExpiration.prototype = {
     else if (aTopic == TOPIC_IDLE_END) {
       // Restart the expiration timer.
       if (!this._timer)
         this._newTimer();
     }
     else if (aTopic == TOPIC_IDLE_DAILY) {
       this._expireWithActionAndLimit(ACTION.IDLE_DAILY, LIMIT.LARGE);
     }
-    else if (aTopic == TOPIC_TESTING_MODE) {
-      this._testingMode = true;
-    }
   },
 
   //////////////////////////////////////////////////////////////////////////////
   //// nsINavHistoryObserver
 
   _inBatchMode: false,
   onBeginUpdateBatch: function PEX_onBeginUpdateBatch()
   {
@@ -813,37 +812,35 @@ nsPlacesExpiration.prototype = {
     else
       this._expireOnIdle = aExpireOnIdle;
     return this._expireOnIdle;
   },
   get expireOnIdle() {
     return this._expireOnIdle;
   },
 
-  _loadPrefs: Task.async(function* () {
+  _loadPrefs: function PEX__loadPrefs() {
     // Get the user's limit, if it was set.
     try {
       // We want to silently fail since getIntPref throws if it does not exist,
       // and use a default to fallback to.
       this._urisLimit = this._prefBranch.getIntPref(PREF_MAX_URIS);
-    } catch(ex) { /* User limit not set */ }
+    }
+    catch(e) {}
 
     if (this._urisLimit < 0) {
-      // Some testing code expects a pref change to be synchronous, so
-      // temporarily set this to a large value, while we asynchronously update
-      // to the correct value.
-      this._urisLimit = 300000;
+      // The preference did not exist or has a negative value.
+      // Calculate the number of unique places that may fit an optimal database
+      // size on this hardware.  If there are more than these unique pages,
+      // some will be expired.
 
-      // The user didn't specify a custom limit, so we calculate the number of
-      // unique places that may fit an optimal database size on this hardware.
-      // Oldest pages over this threshold will be expired.
       let memSizeBytes = MEMSIZE_FALLBACK_BYTES;
       try {
         // Limit the size on systems with small memory.
-         memSizeBytes = Services.sysinfo.getProperty("memsize");
+         memSizeBytes = this._sys.getProperty("memsize");
       } catch (ex) {}
       if (memSizeBytes <= 0) {
         memsize = MEMSIZE_FALLBACK_BYTES;
       }
 
       let diskAvailableBytes = DISKSIZE_FALLBACK_BYTES;
       try {
         // Protect against a full disk or tiny quota.
@@ -856,46 +853,33 @@ nsPlacesExpiration.prototype = {
       }
 
       let optimalDatabaseSize = Math.min(
         memSizeBytes * DATABASE_TO_MEMORY_PERC / 100,
         diskAvailableBytes * DATABASE_TO_DISK_PERC / 100,
         DATABASE_MAX_SIZE
       );
 
-      // Calculate avg size of a URI in the database.
-      let db = yield PlacesUtils.promiseDBConnection();
-      let pageSize = (yield db.execute(`PRAGMA page_size`))[0].getResultByIndex(0);
-      let pageCount = (yield db.execute(`PRAGMA page_count`))[0].getResultByIndex(0);
-      let freelistCount = (yield db.execute(`PRAGMA freelist_count`))[0].getResultByIndex(0);
-      let dbSize = (pageCount - freelistCount) * pageSize;
-      let uriCount = (yield db.execute(`SELECT count(*) FROM moz_places`))[0].getResultByIndex(0);
-      let avgURISize = Math.ceil(dbSize / uriCount);
-      // For new profiles this value may be too large, due to the Sqlite header,
-      // or Infinity when there are no pages.  Thus we must limit it.
-      if (avgURISize > (URIENTRY_AVG_SIZE * 3)) {
-        avgURISize = URIENTRY_AVG_SIZE;
-      }
-      this._urisLimit = Math.ceil(optimalDatabaseSize / avgURISize);
+      this._urisLimit = Math.ceil(optimalDatabaseSize / URIENTRY_AVG_SIZE);
     }
 
     // Expose the calculated limit to other components.
     this._prefBranch.setIntPref(PREF_READONLY_CALCULATED_MAX_URIS,
                                 this._urisLimit);
 
     // Get the expiration interval value.
     try {
       // We want to silently fail since getIntPref throws if it does not exist,
       // and use a default to fallback to.
       this._interval = this._prefBranch.getIntPref(PREF_INTERVAL_SECONDS);
-    } catch (ex) { /* User interval not set */ }
-    if (this._interval <= 0) {
+    }
+    catch (e) {}
+    if (this._interval <= 0)
       this._interval = PREF_INTERVAL_SECONDS_NOTSET;
-    }
-  }),
+  },
 
   /**
    * Evaluates the real number of pages in the database and the value currently
    * used by the SQLite query planner.
    *
    * @param aCallback
    *        invoked on success, function (aPagesCount, aStatsCount).
    */
@@ -1080,36 +1064,31 @@ nsPlacesExpiration.prototype = {
     if (this._shuttingDown)
       return undefined;
     let interval = this.status != STATUS.DIRTY ?
       this._interval * EXPIRE_AGGRESSIVITY_MULTIPLIER : this._interval;
 
     let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
     timer.initWithCallback(this, interval * 1000,
                            Ci.nsITimer.TYPE_REPEATING_SLACK);
-    if (this._testingMode) {
-      Services.obs.notifyObservers(null, TOPIC_TEST_INTERVAL_CHANGED,
-                                   interval);
-    }
     return this._timer = timer;
   },
 
   //////////////////////////////////////////////////////////////////////////////
   //// nsISupports
 
   classID: Components.ID("705a423f-2f69-42f3-b9fe-1517e0dee56f"),
 
   _xpcom_factory: XPCOMUtils.generateSingletonFactory(nsPlacesExpiration),
 
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsIObserver
   , Ci.nsINavHistoryObserver
   , Ci.nsITimerCallback
   , Ci.mozIStorageStatementCallback
-  , Ci.nsISupportsWeakReference
   ])
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Module Registration
 
 var components = [nsPlacesExpiration];
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
--- a/toolkit/components/places/nsPlacesIndexes.h
+++ b/toolkit/components/places/nsPlacesIndexes.h
@@ -11,19 +11,19 @@
   NS_LITERAL_CSTRING( \
     "CREATE " __type " INDEX IF NOT EXISTS " __table "_" __name \
       " ON " __table " (" __columns ")" \
   )
 
 /**
  * moz_places
  */
-#define CREATE_IDX_MOZ_PLACES_URL_HASH \
+#define CREATE_IDX_MOZ_PLACES_URL \
   CREATE_PLACES_IDX( \
-    "url_hashindex", "moz_places", "url_hash", "" \
+    "url_uniqueindex", "moz_places", "url", "UNIQUE" \
   )
 
 #define CREATE_IDX_MOZ_PLACES_FAVICON \
   CREATE_PLACES_IDX( \
     "faviconindex", "moz_places", "favicon_id", "" \
   )
 
 #define CREATE_IDX_MOZ_PLACES_REVHOST \
--- a/toolkit/components/places/nsPlacesTables.h
+++ b/toolkit/components/places/nsPlacesTables.h
@@ -17,17 +17,16 @@
     ", visit_count INTEGER DEFAULT 0" \
     ", hidden INTEGER DEFAULT 0 NOT NULL" \
     ", typed INTEGER DEFAULT 0 NOT NULL" \
     ", favicon_id INTEGER" \
     ", frecency INTEGER DEFAULT -1 NOT NULL" \
     ", last_visit_date INTEGER " \
     ", guid TEXT" \
     ", foreign_count INTEGER DEFAULT 0 NOT NULL" \
-    ", url_hash INTEGER DEFAULT 0 NOT NULL " \
   ")" \
 )
 
 #define CREATE_MOZ_HISTORYVISITS NS_LITERAL_CSTRING( \
   "CREATE TABLE moz_historyvisits (" \
     "  id INTEGER PRIMARY KEY" \
     ", from_visit INTEGER" \
     ", place_id INTEGER" \
--- a/toolkit/components/places/nsTaggingService.js
+++ b/toolkit/components/places/nsTaggingService.js
@@ -61,17 +61,17 @@ TaggingService.prototype = {
       return -1;
     // Using bookmarks service API for this would be a pain.
     // Until tags implementation becomes sane, go the query way.
     let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
                                 .DBConnection;
     let stmt = db.createStatement(
       `SELECT id FROM moz_bookmarks
        WHERE parent = :tag_id
-       AND fk = (SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url)`
+       AND fk = (SELECT id FROM moz_places WHERE url = :page_url)`
     );
     stmt.params.tag_id = tagId;
     stmt.params.page_url = aURI.spec;
     try {
       if (stmt.executeStep()) {
         return stmt.row.id;
       }
     }
@@ -375,17 +375,17 @@ TaggingService.prototype = {
 
     // Using bookmarks service API for this would be a pain.
     // Until tags implementation becomes sane, go the query way.
     let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
                                 .DBConnection;
     let stmt = db.createStatement(
       `SELECT id, parent
        FROM moz_bookmarks
-       WHERE fk = (SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url)`
+       WHERE fk = (SELECT id FROM moz_places WHERE url = :page_url)`
     );
     stmt.params.page_url = aURI.spec;
     try {
       while (stmt.executeStep() && !isBookmarked) {
         if (this._tagFolders[stmt.row.parent]) {
           // This is a tag entry.
           itemIds.push(stmt.row.id);
         }
--- a/toolkit/components/places/tests/PlacesTestUtils.jsm
+++ b/toolkit/components/places/tests/PlacesTestUtils.jsm
@@ -59,17 +59,17 @@ this.PlacesTestUtils = Object.freeze({
       } else if (place.uri instanceof URL) {
         place.uri = NetUtil.newURI(place.uri.href);
       }
       if (typeof place.title != "string") {
         place.title = "test visit for " + place.uri.spec;
       }
       if (typeof place.referrer == "string") {
         place.referrer = NetUtil.newURI(place.referrer);
-      } else if (place.referrer && place.referrer instanceof URL) {
+      } else if (place.referrer instanceof URL) {
         place.referrer = NetUtil.newURI(place.referrer.href);
       }
       place.visits = [{
         transitionType: place.transition === undefined ? Ci.nsINavHistoryService.TRANSITION_LINK
                                                        : place.transition,
         visitDate: place.visitDate || (now++) * 1000,
         referrerURI: place.referrer
       }];
@@ -144,17 +144,17 @@ this.PlacesTestUtils = Object.freeze({
    * @return {Promise}
    * @resolves Returns true if the page is found.
    * @rejects JavaScript exception.
    */
   isPageInDB: Task.async(function* (aURI) {
     let url = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
     let db = yield PlacesUtils.promiseDBConnection();
     let rows = yield db.executeCached(
-      "SELECT id FROM moz_places WHERE url_hash = hash(:url) AND url = :url",
+      "SELECT id FROM moz_places WHERE url = :url",
       { url });
     return rows.length > 0;
   }),
 
   /**
    * Asynchronously checks how many visits exist for a specified page.
    * @param aURI
    *        nsIURI or address to look for.
@@ -164,13 +164,13 @@ this.PlacesTestUtils = Object.freeze({
    * @rejects JavaScript exception.
    */
   visitsInDB: Task.async(function* (aURI) {
     let url = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
     let db = yield PlacesUtils.promiseDBConnection();
     let rows = yield db.executeCached(
       `SELECT count(*) FROM moz_historyvisits v
        JOIN moz_places h ON h.id = v.place_id
-       WHERE url_hash = hash(:url) AND url = :url`,
+       WHERE url = :url`,
       { url });
     return rows[0].getResultByIndex(0);
   })
 });
--- a/toolkit/components/places/tests/bookmarks/test_1017502-bookmarks_foreign_count.js
+++ b/toolkit/components/places/tests/bookmarks/test_1017502-bookmarks_foreign_count.js
@@ -10,18 +10,17 @@ added or removed and also the maintenanc
 */
 
 const T_URI = NetUtil.newURI("https://www.mozilla.org/firefox/nightly/firstrun/");
 
 function* getForeignCountForURL(conn, url) {
   yield PlacesTestUtils.promiseAsyncUpdates();
   url = url instanceof Ci.nsIURI ? url.spec : url;
   let rows = yield conn.executeCached(
-    `SELECT foreign_count FROM moz_places WHERE url_hash = hash(:t_url)
-                                            AND url = :t_url`, { t_url: url });
+      "SELECT foreign_count FROM moz_places WHERE url = :t_url ", { t_url: url });
   return rows[0].getResultByName("foreign_count");
 }
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function* add_remove_change_bookmark_test() {
@@ -64,18 +63,17 @@ add_task(function* maintenance_foreign_c
   let conn = yield PlacesUtils.promiseDBConnection();
 
   // Simulate a visit to the url
   yield PlacesTestUtils.addVisits(T_URI);
 
   // Adjust the foreign_count for the added entry to an incorrect value
   let deferred = Promise.defer();
   let stmt = DBConn().createAsyncStatement(
-    `UPDATE moz_places SET foreign_count = 10 WHERE url_hash = hash(:t_url)
-                                                AND url = :t_url `);
+    "UPDATE moz_places SET foreign_count = 10 WHERE url = :t_url ");
   stmt.params.t_url = T_URI.spec;
   stmt.executeAsync({
     handleCompletion: function(){
       deferred.resolve();
     }
   });
   stmt.finalize();
   yield deferred.promise;
--- a/toolkit/components/places/tests/browser/browser_bug248970.js
+++ b/toolkit/components/places/tests/browser/browser_bug248970.js
@@ -115,24 +115,45 @@ function getPlacesItemsCount() {
   return cc;
 }
 
 function* checkHistoryItems() {
   for (let i = 0; i < visitedURIs.length; i++) {
     let visitedUri = visitedURIs[i];
     ok((yield promiseIsURIVisited(visitedUri)), "");
     if (/embed/.test(visitedUri.spec)) {
-      is((yield PlacesTestUtils.isPageInDB(visitedUri)), false, "Check if URI is in database");
+      is(!!pageInDatabase(visitedUri), false, "Check if URI is in database");
     } else {
-      ok((yield PlacesTestUtils.isPageInDB(visitedUri)), "Check if URI is in database");
+      ok(!!pageInDatabase(visitedUri), "Check if URI is in database");
     }
   }
 }
 
 /**
+ * Checks if an address is found in the database.
+ * @param aURI
+ *        nsIURI or address to look for.
+ * @return place id of the page or 0 if not found
+ */
+function pageInDatabase(aURI) {
+  let url = (aURI instanceof Ci.nsIURI ? aURI.spec : aURI);
+  let stmt = DBConn().createStatement(
+    "SELECT id FROM moz_places WHERE url = :url"
+  );
+  stmt.params.url = url;
+  try {
+    if (!stmt.executeStep())
+      return 0;
+    return stmt.getInt64(0);
+  } finally {
+    stmt.finalize();
+  }
+}
+
+/**
  * Function attempts to check if Bookmark-A has been visited
  * during private browsing mode, function should return false
  *
  * @returns false if the accessCount has not changed
  *          true if the accessCount has changed
  */
 function isBookmarkAltered(){
   let options = PlacesUtils.history.getNewQueryOptions();
--- a/toolkit/components/places/tests/browser/browser_double_redirect.js
+++ b/toolkit/components/places/tests/browser/browser_double_redirect.js
@@ -29,17 +29,17 @@ add_task(function* () {
           // Get all pages visited from the original typed one
           let db = yield PlacesUtils.promiseDBConnection();
           let rows = yield db.execute(
             `SELECT url FROM moz_historyvisits
              JOIN moz_places h ON h.id = place_id
              WHERE from_visit IN
                 (SELECT v.id FROM moz_historyvisits v
                  JOIN moz_places p ON p.id = v.place_id
-                 WHERE p.url_hash = hash(:url) AND p.url = :url)
+                 WHERE p.url = :url)
             `, { url: TEST_URI.spec });
 
           is(rows.length, 1, "Found right number of visits");
           let visitedUrl = rows[0].getResultByName("url");
           // Check that redirect from_visit is not from the original typed one
           is(visitedUrl, FIRST_REDIRECTING_URI.spec, "Check referrer for " + visitedUrl);
 
           resolve();
--- a/toolkit/components/places/tests/browser/browser_history_post.js
+++ b/toolkit/components/places/tests/browser/browser_history_post.js
@@ -13,11 +13,16 @@ add_task(function* () {
           resolve();
         });
       });
       submit.click();
       yield p;
     });
     let visited = yield promiseIsURIVisited(SJS_URI);
     ok(!visited, "The POST page should not be added to history");
-    ok(!(yield PlacesTestUtils.isPageInDB(SJS_URI.spec)), "The page should not be in the database");
+    let db = yield PlacesUtils.promiseDBConnection();
+    let rows = yield db.execute(
+      "SELECT 1 FROM moz_places WHERE url = :page_url",
+      {page_url: SJS_URI.spec});
+    is(rows.length, 0, "The page should not be in the database");
+    yield db.close();
   }));
 });
--- a/toolkit/components/places/tests/browser/browser_settitle.js
+++ b/toolkit/components/places/tests/browser/browser_settitle.js
@@ -1,19 +1,25 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
 var conn = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase).DBConnection;
 
 /**
  * Gets a single column value from either the places or historyvisits table.
  */
-function getColumn(table, column, url)
+function getColumn(table, column, fromColumnName, fromColumnValue)
 {
   var stmt = conn.createStatement(
-    `SELECT ${column} FROM ${table} WHERE url_hash = hash(:val) AND url = :val`);
+    `SELECT ${column} FROM ${table} WHERE ${fromColumnName} = :val
+     LIMIT 1`);
   try {
-    stmt.params.val = url;
+    stmt.params.val = fromColumnValue;
     stmt.executeStep();
     return stmt.row[column];
   }
   finally {
     stmt.finalize();
   }
 }
 
@@ -58,19 +64,19 @@ add_task(function* ()
   const url2 = "http://example.com/tests/toolkit/components/places/tests/browser/title2.html";
   let loadPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
   BrowserTestUtils.loadURI(gBrowser.selectedBrowser, url2);
   yield loadPromise;
 
   let data = yield titleChangedPromise;
   is(data[0].uri.spec, "http://example.com/tests/toolkit/components/places/tests/browser/title2.html");
   is(data[0].title, "Some title");
-  is(data[0].guid, getColumn("moz_places", "guid", data[0].uri.spec));
+  is(data[0].guid, getColumn("moz_places", "guid", "url", data[0].uri.spec));
 
   data.forEach(function(item) {
-    var title = getColumn("moz_places", "title", data[0].uri.spec);
+    var title = getColumn("moz_places", "title", "url", data[0].uri.spec);
     is(title, item.title);
   });
 
   gBrowser.removeCurrentTab();
   yield PlacesTestUtils.clearHistory();
 });
 
--- a/toolkit/components/places/tests/browser/browser_visituri.js
+++ b/toolkit/components/places/tests/browser/browser_visituri.js
@@ -1,8 +1,13 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
 /**
  * One-time observer callback.
  */
 function promiseObserve(name, checkFn) {
   return new Promise(resolve => {
     Services.obs.addObserver(function observer(subject) {
       if (checkFn(subject)) {
         Services.obs.removeObserver(observer, name);
@@ -16,17 +21,16 @@ var conn = PlacesUtils.history.QueryInte
 
 /**
  * Gets a single column value from either the places or historyvisits table.
  */
 function getColumn(table, column, fromColumnName, fromColumnValue) {
   let sql = `SELECT ${column}
              FROM ${table}
              WHERE ${fromColumnName} = :val
-             ${fromColumnName == "url" ? "AND url_hash = hash(:val)" : ""}
              LIMIT 1`;
   let stmt = conn.createStatement(sql);
   try {
     stmt.params.val = fromColumnValue;
     ok(stmt.executeStep(), "Expect to get a row");
     return stmt.row[column];
   }
   finally {
--- a/toolkit/components/places/tests/browser/head.js
+++ b/toolkit/components/places/tests/browser/head.js
@@ -23,17 +23,17 @@ const TRANSITION_DOWNLOAD = PlacesUtils.
  * param aCallback
  *        Callback function that will get the property value.
  */
 function fieldForUrl(aURI, aFieldName, aCallback)
 {
   let url = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
   let stmt = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
                                 .DBConnection.createAsyncStatement(
-    `SELECT ${aFieldName} FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url`
+    `SELECT ${aFieldName} FROM moz_places WHERE url = :page_url`
   );
   stmt.params.page_url = url;
   stmt.executeAsync({
     _value: -1,
     handleResult: function(aResultSet) {
       let row = aResultSet.getNextRow();
       if (!row)
         ok(false, "The page should exist in the database");
@@ -211,17 +211,17 @@ function checkGuidForURI(aURI, aGUID) {
  * @param aURI
  *        The uri to check.
  * @return the associated the guid.
  */
 function doGetGuidForURI(aURI) {
   let stmt = DBConn().createStatement(
     `SELECT guid
        FROM moz_places
-       WHERE url_hash = hash(:url) AND url = :url`
+       WHERE url = :url`
   );
   stmt.params.url = aURI.spec;
   ok(stmt.executeStep(), "Check get guid for uri from moz_places");
   let guid = stmt.row.guid;
   stmt.finalize();
   doCheckValidPlacesGuid(guid);
   return guid;
 }
--- a/toolkit/components/places/tests/cpp/places_test_harness.h
+++ b/toolkit/components/places/tests/cpp/places_test_harness.h
@@ -264,17 +264,17 @@ do_get_place(nsIURI* aURI, PlaceRecord& 
   nsCOMPtr<mozIStorageStatement> stmt;
 
   nsCString spec;
   nsresult rv = aURI->GetSpec(spec);
   do_check_success(rv);
 
   rv = dbConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT id, hidden, typed, visit_count, guid FROM moz_places "
-    "WHERE url_hash = hash(?1) AND url = ?1"
+    "WHERE url=?1 "
   ), getter_AddRefs(stmt));
   do_check_success(rv);
 
   rv = stmt->BindUTF8StringByIndex(0, spec);
   do_check_success(rv);
 
   bool hasResults;
   rv = stmt->ExecuteStep(&hasResults);
--- a/toolkit/components/places/tests/expiration/head_expiration.js
+++ b/toolkit/components/places/tests/expiration/head_expiration.js
@@ -29,19 +29,17 @@ function shutdownExpiration()
 }
 
 
 /**
  * Causes expiration component to start, otherwise it would wait for the first
  * history notification.
  */
 function force_expiration_start() {
-  Cc["@mozilla.org/places/expiration;1"]
-    .getService(Ci.nsIObserver)
-    .observe(null, "testing-mode", null);
+  Cc["@mozilla.org/places/expiration;1"].getService(Ci.nsISupports);
 }
 
 
 /**
  * Forces an expiration run.
  *
  * @param [optional] aLimit
  *        Limit for the expiration.  Pass -1 for unlimited.
--- a/toolkit/components/places/tests/expiration/test_annos_expire_policy.js
+++ b/toolkit/components/places/tests/expiration/test_annos_expire_policy.js
@@ -47,17 +47,17 @@ function add_old_anno(aIdentifier, aName
   }
   else if (aIdentifier instanceof Ci.nsIURI){
     // Page annotation.
     as.setPageAnnotation(aIdentifier, aName, aValue, 0, aExpirePolicy);
     // Update dateAdded for the last added annotation.
     sql = "UPDATE moz_annos SET dateAdded = :expire_date, lastModified = :last_modified " +
           "WHERE id = (SELECT a.id FROM moz_annos a " +
                       "LEFT JOIN moz_places h on h.id = a.place_id " +
-                      "WHERE h.url_hash = hash(:id) AND h.url = :id " +
+                      "WHERE h.url = :id " +
                       "ORDER BY a.dateAdded DESC LIMIT 1)";
   }
   else
     do_throw("Wrong identifier type");
 
   let stmt = DBConn().createStatement(sql);
   stmt.params.id = (typeof(aIdentifier) == "number") ? aIdentifier
                                                      : aIdentifier.spec;
--- a/toolkit/components/places/tests/expiration/test_clearHistory.js
+++ b/toolkit/components/places/tests/expiration/test_clearHistory.js
@@ -50,17 +50,17 @@ function add_old_anno(aIdentifier, aName
     // Page annotation.
     as.setPageAnnotation(aIdentifier, aName, aValue, 0, aExpirePolicy);
     // Update dateAdded for the last added annotation.
     sql = "UPDATE moz_annos SET dateAdded = :expire_date, lastModified = :last_modified " +
           "WHERE id = ( " +
             "SELECT a.id FROM moz_annos a " +
             "JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id " +
             "JOIN moz_places h on h.id = a.place_id " +
-            "WHERE h.url_hash = hash(:id) AND h.url = :id " +
+            "WHERE h.url = :id " +
             "AND n.name = :anno_name " +
             "ORDER BY a.dateAdded DESC LIMIT 1 " +
           ")";
   }
   else
     do_throw("Wrong identifier type");
 
   let stmt = DBConn().createStatement(sql);
--- a/toolkit/components/places/tests/expiration/test_outdated_analyze.js
+++ b/toolkit/components/places/tests/expiration/test_outdated_analyze.js
@@ -47,17 +47,17 @@ function run_test() {
   Services.obs.addObserver(function observeExpiration(aSubject, aTopic, aData) {
     Services.obs.removeObserver(observeExpiration,
                                 PlacesUtils.TOPIC_EXPIRATION_FINISHED);
 
     // Check that statistica are up-to-date.
     let stmt = DBConn().createAsyncStatement(
       "SELECT (SELECT COUNT(*) FROM moz_places) - "
       +        "(SELECT SUBSTR(stat,1,LENGTH(stat)-2) FROM sqlite_stat1 "
-      +         "WHERE idx = 'moz_places_url_hashindex')"
+      +         "WHERE idx = 'moz_places_url_uniqueindex')"
     );
     stmt.executeAsync({
       handleResult: function(aResultSet) {
         let row = aResultSet.getNextRow();
         this._difference = row.getResultByIndex(0);
       },
       handleError: function(aError) {
         do_throw("Unexpected error (" + aError.result + "): " + aError.message);
--- a/toolkit/components/places/tests/expiration/test_pref_interval.js
+++ b/toolkit/components/places/tests/expiration/test_pref_interval.js
@@ -1,8 +1,14 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
 /**
  * What this is aimed to test:
  *
  * Expiration relies on an interval, that is user-preffable setting
  * "places.history.expiration.interval_seconds".
  * On pref change it will stop current interval timer and fire a new one,
  * that will obey the new value.
  * If the pref is set to a number <= 0 we will use the default value.
@@ -10,16 +16,56 @@
 
 // Default timer value for expiration in seconds.  Must have same value as
 // PREF_INTERVAL_SECONDS_NOTSET in nsPlacesExpiration.
 const DEFAULT_TIMER_DELAY_SECONDS = 3 * 60;
 
 // Sync this with the const value in the component.
 const EXPIRE_AGGRESSIVITY_MULTIPLIER = 3;
 
+Cu.import("resource://testing-common/MockRegistrar.jsm");
+// Provide a mock timer implementation, so there is no need to wait seconds to
+// achieve test results.
+const TIMER_CONTRACT_ID = "@mozilla.org/timer;1";
+var mockCID;
+
+var mockTimerImpl = {
+  initWithCallback: function MTI_initWithCallback(aCallback, aDelay, aType) {
+    print("Checking timer delay equals expected interval value");
+    if (!currentTest)
+      return;
+    // History status is not dirty, so the timer is delayed.
+    do_check_eq(aDelay, currentTest.expectedTimerDelay * 1000 * EXPIRE_AGGRESSIVITY_MULTIPLIER)
+
+    do_execute_soon(runNextTest);
+  },
+
+  cancel: function() {},
+  initWithFuncCallback: function() {},
+  init: function() {},
+
+  QueryInterface: XPCOMUtils.generateQI([
+    Ci.nsITimer,
+  ])
+}
+
+function replace_timer_factory() {
+  mockCID = MockRegistrar.register(TIMER_CONTRACT_ID, mockTimerImpl);
+}
+
+do_register_cleanup(function() {
+  // Shutdown expiration before restoring original timer, otherwise we could
+  // leak due to the different implementation.
+  shutdownExpiration();
+
+  // Restore original timer factory.
+  MockRegistrar.unregister(mockCID);
+});
+
+
 var tests = [
 
   // This test should be the first, so the interval won't be influenced by
   // status of history.
   { desc: "Set interval to 1s.",
     interval: 1,
     expectedTimerDelay: 1
   },
@@ -38,26 +84,37 @@ var tests = [
     interval: 100,
     expectedTimerDelay: 100
   },
 
 ];
 
 var currentTest;
 
-add_task(function* test() {
+function run_test() {
   // The pref should not exist by default.
-  Assert.throws(() => getInterval());
+  try {
+    getInterval();
+    do_throw("interval pref should not exist by default");
+  }
+  catch (ex) {}
+
+  // Use our own mock timer implementation.
+  replace_timer_factory();
 
   // Force the component, so it will start observing preferences.
   force_expiration_start();
 
-  for (let currentTest of tests) {
+  runNextTest();
+  do_test_pending();
+}
+
+function runNextTest() {
+  if (tests.length) {
+    currentTest = tests.shift();
     print(currentTest.desc);
-    let promise = promiseTopicObserved("test-interval-changed");
     setInterval(currentTest.interval);
-    let [, data] = yield promise;
-    Assert.equal(data, currentTest.expectedTimerDelay * EXPIRE_AGGRESSIVITY_MULTIPLIER);
   }
-
-  clearInterval();
-});
-
+  else {
+    clearInterval();
+    do_test_finished();
+  }
+}
--- a/toolkit/components/places/tests/expiration/test_pref_maxpages.js
+++ b/toolkit/components/places/tests/expiration/test_pref_maxpages.js
@@ -83,17 +83,17 @@ add_task(function* test_pref_maxpages() 
     // Setup visits.
     let now = getExpirablePRTime();
     for (let i = 0; i < currentTest.addPages; i++) {
       let page = "http://" + testIndex + "." + i + ".mozilla.org/";
       yield PlacesTestUtils.addVisits({ uri: uri(page), visitDate: now++ });
     }
 
     // Observe history.
-    let historyObserver = {
+    historyObserver = {
       onBeginUpdateBatch: function PEX_onBeginUpdateBatch() {},
       onEndUpdateBatch: function PEX_onEndUpdateBatch() {},
       onClearHistory: function() {},
       onVisit: function() {},
       onTitleChanged: function() {},
       onDeleteURI: function(aURI) {
         print("onDeleteURI " + aURI.spec);
         currentTest.receivedNotifications++;
--- a/toolkit/components/places/tests/expiration/xpcshell.ini
+++ b/toolkit/components/places/tests/expiration/xpcshell.ini
@@ -13,9 +13,11 @@ skip-if = os == "android"
 [test_clearHistory.js]
 [test_debug_expiration.js]
 [test_idle_daily.js]
 [test_notifications.js]
 [test_notifications_onDeleteURI.js]
 [test_notifications_onDeleteVisits.js]
 [test_outdated_analyze.js]
 [test_pref_interval.js]
+# Crashes when timer is used on non-main thread due to JS implemetation in this test
+skip-if = "JS implementation of nsITimer"
 [test_pref_maxpages.js]
--- a/toolkit/components/places/tests/head_common.js
+++ b/toolkit/components/places/tests/head_common.js
@@ -1,14 +1,14 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-const CURRENT_SCHEMA_VERSION = 33;
+const CURRENT_SCHEMA_VERSION = 32;
 const FIRST_UPGRADABLE_SCHEMA_VERSION = 11;
 
 const NS_APP_USER_PROFILE_50_DIR = "ProfD";
 const NS_APP_PROFILE_DIR_STARTUP = "ProfDS";
 
 // Shortcuts to transitions type.
 const TRANSITION_LINK = Ci.nsINavHistoryService.TRANSITION_LINK;
 const TRANSITION_TYPED = Ci.nsINavHistoryService.TRANSITION_TYPED;
@@ -294,17 +294,17 @@ function dump_table(aName)
  * @param aURI
  *        nsIURI or address to look for.
  * @return place id of the page or 0 if not found
  */
 function page_in_database(aURI)
 {
   let url = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
   let stmt = DBConn().createStatement(
-    "SELECT id FROM moz_places WHERE url_hash = hash(:url) AND url = :url"
+    "SELECT id FROM moz_places WHERE url = :url"
   );
   stmt.params.url = url;
   try {
     if (!stmt.executeStep())
       return 0;
     return stmt.getInt64(0);
   }
   finally {
@@ -319,17 +319,17 @@ function page_in_database(aURI)
  * @return number of visits found.
  */
 function visits_in_database(aURI)
 {
   let url = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
   let stmt = DBConn().createStatement(
     `SELECT count(*) FROM moz_historyvisits v
      JOIN moz_places h ON h.id = v.place_id
-     WHERE url_hash = hash(:url) AND url = :url`
+     WHERE url = :url`
   );
   stmt.params.url = url;
   try {
     if (!stmt.executeStep())
       return 0;
     return stmt.getInt64(0);
   }
   finally {
@@ -537,17 +537,17 @@ function frecencyForUrl(aURI)
 {
   let url = aURI;
   if (aURI instanceof Ci.nsIURI) {
     url = aURI.spec;
   } else if (aURI instanceof URL) {
     url = aURI.href;
   }
   let stmt = DBConn().createStatement(
-    "SELECT frecency FROM moz_places WHERE url_hash = hash(?1) AND url = ?1"
+    "SELECT frecency FROM moz_places WHERE url = ?1"
   );
   stmt.bindByIndex(0, url);
   try {
     if (!stmt.executeStep()) {
       throw new Error("No result for frecency.");
     }
     return stmt.getInt32(0);
   } finally {
@@ -561,17 +561,17 @@ function frecencyForUrl(aURI)
  * @param aURI
  *        The URI or spec to get hidden for.
  * @return @return true if the url is hidden, false otherwise.
  */
 function isUrlHidden(aURI)
 {
   let url = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
   let stmt = DBConn().createStatement(
-    "SELECT hidden FROM moz_places WHERE url_hash = hash(?1) AND url = ?1"
+    "SELECT hidden FROM moz_places WHERE url = ?1"
   );
   stmt.bindByIndex(0, url);
   if (!stmt.executeStep())
     throw new Error("No result for hidden.");
   let hidden = stmt.getInt32(0);
   stmt.finalize();
 
   return !!hidden;
@@ -638,17 +638,17 @@ function do_get_guid_for_uri(aURI,
                              aStack)
 {
   if (!aStack) {
     aStack = Components.stack.caller;
   }
   let stmt = DBConn().createStatement(
     `SELECT guid
      FROM moz_places
-     WHERE url_hash = hash(:url) AND url = :url`
+     WHERE url = :url`
   );
   stmt.params.url = aURI.spec;
   do_check_true(stmt.executeStep(), aStack);
   let guid = stmt.row.guid;
   stmt.finalize();
   do_check_valid_places_guid(guid, aStack);
   return guid;
 }
@@ -861,12 +861,12 @@ function checkBookmarkObject(info) {
  * Reads foreign_count value for a given url.
  */
 function* foreign_count(url) {
   if (url instanceof Ci.nsIURI)
     url = url.spec;
   let db = yield PlacesUtils.promiseDBConnection();
   let rows = yield db.executeCached(
     `SELECT foreign_count FROM moz_places
-     WHERE url_hash = hash(:url) AND url = :url
+     WHERE url = :url
     `, { url });
   return rows.length == 0 ? 0 : rows[0].getResultByName("foreign_count");
 }
deleted file mode 100644
index 6071dc6a68566d834ae751a0b649e3ae79c33140..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/toolkit/components/places/tests/migration/xpcshell.ini
+++ b/toolkit/components/places/tests/migration/xpcshell.ini
@@ -14,17 +14,16 @@ support-files =
   places_v24.sqlite
   places_v25.sqlite
   places_v26.sqlite
   places_v27.sqlite
   places_v28.sqlite
   places_v30.sqlite
   places_v31.sqlite
   places_v32.sqlite
-  places_v33.sqlite
 
 [test_current_from_downgraded.js]
 [test_current_from_v6.js]
 [test_current_from_v11.js]
 [test_current_from_v19.js]
 [test_current_from_v24.js]
 [test_current_from_v25.js]
 [test_current_from_v26.js]
--- a/toolkit/components/places/tests/queries/head_queries.js
+++ b/toolkit/components/places/tests/queries/head_queries.js
@@ -55,17 +55,17 @@ function* task_populateDB(aArray)
           visitDate: qdata.lastVisit,
           referrer: qdata.referrer ? uri(qdata.referrer) : null,
           title: qdata.title
         });
         if (qdata.visitCount && !qdata.isDetails) {
           // Set a fake visit_count, this is not a real count but can be used
           // to test sorting by visit_count.
           let stmt = DBConn().createAsyncStatement(
-            "UPDATE moz_places SET visit_count = :vc WHERE url_hash = hash(:url) AND url = :url");
+            "UPDATE moz_places SET visit_count = :vc WHERE url = :url");
           stmt.params.vc = qdata.visitCount;
           stmt.params.url = qdata.uri;
           try {
             stmt.executeAsync();
           }
           catch (ex) {
             print("Error while setting visit_count.");
           }
@@ -74,17 +74,17 @@ function* task_populateDB(aArray)
           }
         }
       }
 
       if (qdata.isRedirect) {
         // This must be async to properly enqueue after the updateFrecency call
         // done by the visit addition.
         let stmt = DBConn().createAsyncStatement(
-          "UPDATE moz_places SET hidden = 1 WHERE url_hash = hash(:url) AND url = :url");
+          "UPDATE moz_places SET hidden = 1 WHERE url = :url");
         stmt.params.url = qdata.uri;
         try {
           stmt.executeAsync();
         }
         catch (ex) {
           print("Error while setting hidden.");
         }
         finally {
--- a/toolkit/components/places/tests/unit/test_async_history_api.js
+++ b/toolkit/components/places/tests/unit/test_async_history_api.js
@@ -1,8 +1,11 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
 /**
  * This file tests the async history API exposed by mozIAsyncHistory.
  */
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Globals
 
 const TEST_DOMAIN = "http://mozilla.org/";
@@ -123,17 +126,17 @@ VisitObserver.prototype = {
  */
 function do_check_title_for_uri(aURI,
                                 aTitle)
 {
   let stack = Components.stack.caller;
   let stmt = DBConn().createStatement(
     `SELECT title
      FROM moz_places
-     WHERE url_hash = hash(:url) AND url = :url`
+     WHERE url = :url`
   );
   stmt.params.url = aURI.spec;
   do_check_true(stmt.executeStep(), stack);
   do_check_eq(stmt.row.title, aTitle, stack);
   stmt.finalize();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -540,18 +543,17 @@ add_task(function* test_old_referrer_ign
   do_check_true(yield promiseIsURIVisited(place.uri));
 
   // Though the visit will not contain the referrer, we must examine the
   // database to be sure.
   do_check_eq(placeInfo.visits[0].referrerURI, null);
   let stmt = DBConn().createStatement(
     `SELECT COUNT(1) AS count
      FROM moz_historyvisits
-     JOIN moz_places h ON h.id = place_id
-     WHERE url_hash = hash(:page_url) AND url = :page_url
+     WHERE place_id = (SELECT id FROM moz_places WHERE url = :page_url)
      AND from_visit = 0`
   );
   stmt.params.page_url = place.uri.spec;
   do_check_true(stmt.executeStep());
   do_check_eq(stmt.row.count, 1);
   stmt.finalize();
 
   yield PlacesTestUtils.promiseAsyncUpdates();
@@ -733,45 +735,45 @@ add_task(function* test_properties_saved
     const EXPECTED_COUNT = visit.transitionType == TRANSITION_EMBED ? 0 : 1;
 
     // mozIVisitInfo::date
     let stmt = DBConn().createStatement(
       `SELECT COUNT(1) AS count
        FROM moz_places h
        JOIN moz_historyvisits v
        ON h.id = v.place_id
-       WHERE h.url_hash = hash(:page_url) AND h.url = :page_url
+       WHERE h.url = :page_url
        AND v.visit_date = :visit_date`
     );
     stmt.params.page_url = uri.spec;
     stmt.params.visit_date = visit.visitDate;
     do_check_true(stmt.executeStep());
     do_check_eq(stmt.row.count, EXPECTED_COUNT);
     stmt.finalize();
 
     // mozIVisitInfo::transitionType
     stmt = DBConn().createStatement(
       `SELECT COUNT(1) AS count
        FROM moz_places h
        JOIN moz_historyvisits v
        ON h.id = v.place_id
-       WHERE h.url_hash = hash(:page_url) AND h.url = :page_url
+       WHERE h.url = :page_url
        AND v.visit_type = :transition_type`
     );
     stmt.params.page_url = uri.spec;
     stmt.params.transition_type = visit.transitionType;
     do_check_true(stmt.executeStep());
     do_check_eq(stmt.row.count, EXPECTED_COUNT);
     stmt.finalize();
 
     // mozIPlaceInfo::title
     stmt = DBConn().createStatement(
       `SELECT COUNT(1) AS count
        FROM moz_places h
-       WHERE h.url_hash = hash(:page_url) AND h.url = :page_url
+       WHERE h.url = :page_url
        AND h.title = :title`
     );
     stmt.params.page_url = uri.spec;
     stmt.params.title = placeInfo.title;
     do_check_true(stmt.executeStep());
     do_check_eq(stmt.row.count, EXPECTED_COUNT);
     stmt.finalize();
 
@@ -834,23 +836,21 @@ add_task(function* test_referrer_saved()
 
     // We need to insert all of our visits before we can test conditions.
     if (++resultCount == places.length) {
       do_check_true(places[0].uri.equals(visit.referrerURI));
 
       let stmt = DBConn().createStatement(
         `SELECT COUNT(1) AS count
          FROM moz_historyvisits
-         JOIN moz_places h ON h.id = place_id
-         WHERE url_hash = hash(:page_url) AND url = :page_url
+         WHERE place_id = (SELECT id FROM moz_places WHERE url = :page_url)
          AND from_visit = (
-           SELECT v.id
-           FROM moz_historyvisits v
-           JOIN moz_places h ON h.id = place_id
-           WHERE url_hash = hash(:referrer) AND url = :referrer
+           SELECT id
+           FROM moz_historyvisits
+           WHERE place_id = (SELECT id FROM moz_places WHERE url = :referrer)
          )`
       );
       stmt.params.page_url = uri.spec;
       stmt.params.referrer = visit.referrerURI.spec;
       do_check_true(stmt.executeStep());
       do_check_eq(stmt.row.count, 1);
       stmt.finalize();
 
@@ -1112,19 +1112,18 @@ add_task(function* test_typed_hidden_not
       visits: [
         new VisitInfo(TRANSITION_FRAMED_LINK)
       ]
     },
   ];
   yield promiseUpdatePlaces(places);
 
   let db = yield PlacesUtils.promiseDBConnection();
-  let rows = yield db.execute(
-    "SELECT hidden, typed FROM moz_places WHERE url_hash = hash(:url) AND url = :url",
-    { url: "http://mozilla.org/" });
+  let rows = yield db.execute("SELECT hidden, typed FROM moz_places WHERE url = :url",
+                              { url: "http://mozilla.org/" });
   Assert.equal(rows[0].getResultByName("typed"), 1,
                "The page should be marked as typed");
   Assert.equal(rows[0].getResultByName("hidden"), 0,
                "The page should be marked as not hidden");
 });
 
 function run_test()
 {
--- a/toolkit/components/places/tests/unit/test_history_clear.js
+++ b/toolkit/components/places/tests/unit/test_history_clear.js
@@ -126,18 +126,17 @@ add_task(function* test_history_clear()
   // Check that history tables are empty
   stmt = mDBConn.createStatement(
     "SELECT * FROM (SELECT id FROM moz_historyvisits LIMIT 1)");
   do_check_false(stmt.executeStep());
   stmt.finalize();
 
   // Check that all moz_places entries except bookmarks and place: have been removed
   stmt = mDBConn.createStatement(
-    `SELECT h.id FROM moz_places h WHERE
-       url_hash NOT BETWEEN hash('place', 'prefix_lo') AND hash('place', 'prefix_hi')
+    `SELECT h.id FROM moz_places h WHERE SUBSTR(h.url, 1, 6) <> 'place:'
        AND NOT EXISTS (SELECT id FROM moz_bookmarks WHERE fk = h.id) LIMIT 1`);
   do_check_false(stmt.executeStep());
   stmt.finalize();
 
   // Check that we only have favicons for retained places
   stmt = mDBConn.createStatement(
     `SELECT f.id FROM moz_favicons f WHERE NOT EXISTS
        (SELECT id FROM moz_places WHERE favicon_id = f.id) LIMIT 1`);
@@ -156,14 +155,12 @@ add_task(function* test_history_clear()
     `SELECT i.place_id FROM moz_inputhistory i WHERE NOT EXISTS
        (SELECT id FROM moz_places WHERE id = i.place_id) LIMIT 1`);
   do_check_false(stmt.executeStep());
   stmt.finalize();
 
   // Check that place:uris have frecency 0
   stmt = mDBConn.createStatement(
     `SELECT h.id FROM moz_places h
-     WHERE url_hash BETWEEN hash('place', 'prefix_lo')
-                        AND hash('place', 'prefix_hi')
-       AND h.frecency <> 0 LIMIT 1`);
+     WHERE SUBSTR(h.url, 1, 6) = 'place:' AND h.frecency <> 0 LIMIT 1`);
   do_check_false(stmt.executeStep());
   stmt.finalize();
 });
--- a/toolkit/components/places/tests/unit/test_hosts_triggers.js
+++ b/toolkit/components/places/tests/unit/test_hosts_triggers.js
@@ -13,17 +13,17 @@ XPCOMUtils.defineLazyServiceGetter(this,
 // change its uri, then remove it, and
 // for each change check that moz_hosts has correctly been updated.
 
 function isHostInMozPlaces(aURI)
 {
   let stmt = DBConn().createStatement(
     `SELECT url
        FROM moz_places
-       WHERE url_hash = hash(:host) AND url = :host`
+       WHERE url = :host`
   );
   let result = false;
   stmt.params.host = aURI.spec;
   while(stmt.executeStep()) {
     if (stmt.row.url == aURI.spec) {
       result = true;
       break;
     }
--- a/toolkit/components/places/tests/unit/test_preventive_maintenance.js
+++ b/toolkit/components/places/tests/unit/test_preventive_maintenance.js
@@ -37,17 +37,17 @@ function cleanDatabase() {
   mDBConn.executeSimpleSQL("DELETE FROM moz_inputhistory");
   mDBConn.executeSimpleSQL("DELETE FROM moz_keywords");
   mDBConn.executeSimpleSQL("DELETE FROM moz_favicons");
   mDBConn.executeSimpleSQL("DELETE FROM moz_bookmarks WHERE id > " + defaultBookmarksMaxId);
 }
 
 function addPlace(aUrl, aFavicon) {
   let stmt = mDBConn.createStatement(
-    "INSERT INTO moz_places (url, url_hash, favicon_id) VALUES (:url, hash(:url), :favicon)");
+    "INSERT INTO moz_places (url, favicon_id) VALUES (:url, :favicon)");
   stmt.params["url"] = aUrl || "http://www.mozilla.org";
   stmt.params["favicon"] = aFavicon || null;
   stmt.execute();
   stmt.finalize();
   return mDBConn.lastInsertRowID;
 }
 
 function addBookmark(aPlaceId, aType, aParent, aKeywordId, aFolderType, aTitle) {
@@ -1072,28 +1072,26 @@ tests.push({
 
 tests.push({
   name: "L.2",
   desc: "Recalculate visit_count and last_visit_date",
 
   setup: function* () {
     function setVisitCount(aURL, aValue) {
       let stmt = mDBConn.createStatement(
-        `UPDATE moz_places SET visit_count = :count WHERE url_hash = hash(:url)
-                                                      AND url = :url`
+        "UPDATE moz_places SET visit_count = :count WHERE url = :url"
       );
       stmt.params.count = aValue;
       stmt.params.url = aURL;
       stmt.execute();
       stmt.finalize();
     }
     function setLastVisitDate(aURL, aValue) {
       let stmt = mDBConn.createStatement(
-        `UPDATE moz_places SET last_visit_date = :date WHERE url_hash = hash(:url)
-                                                         AND url = :url`
+        "UPDATE moz_places SET last_visit_date = :date WHERE url = :url"
       );
       stmt.params.date = aValue;
       stmt.params.url = aURL;
       stmt.execute();
       stmt.finalize();
     }
 
     let now = Date.now() * 1000;
@@ -1148,18 +1146,18 @@ tests.push({
 });
 
 //------------------------------------------------------------------------------
 
 tests.push({
   name: "L.3",
   desc: "recalculate hidden for redirects.",
 
-  *setup() {
-    yield PlacesTestUtils.addVisits([
+  setup: function() {
+    PlacesTestUtils.addVisits([
       { uri: NetUtil.newURI("http://l3.moz.org/"),
         transition: TRANSITION_TYPED },
       { uri: NetUtil.newURI("http://l3.moz.org/redirecting/"),
         transition: TRANSITION_TYPED },
       { uri: NetUtil.newURI("http://l3.moz.org/redirecting2/"),
         transition: TRANSITION_REDIRECT_TEMPORARY,
         referrer: NetUtil.newURI("http://l3.moz.org/redirecting/") },
       { uri: NetUtil.newURI("http://l3.moz.org/target/"),
@@ -1195,71 +1193,16 @@ tests.push({
       stmt.finalize();
     });
   }
 });
 
 //------------------------------------------------------------------------------
 
 tests.push({
-  name: "L.4",
-  desc: "recalculate foreign_count.",
-
-  *setup() {
-    this._pageGuid = (yield PlacesUtils.history.insert({ url: "http://l4.moz.org/",
-                                                         visits: [{ date: new Date() }] })).guid;
-    yield PlacesUtils.bookmarks.insert({ url: "http://l4.moz.org/",
-                                         parentGuid: PlacesUtils.bookmarks.unfiledGuid});
-    yield PlacesUtils.keywords.insert({ url: "http://l4.moz.org/", keyword: "kw" });
-    Assert.equal((yield this._getForeignCount()), 2);
-  },
-
-  *_getForeignCount() {
-    let db = yield PlacesUtils.promiseDBConnection();
-    let rows = yield db.execute(`SELECT foreign_count FROM moz_places
-                                 WHERE guid = :guid`, { guid: this._pageGuid });
-    return rows[0].getResultByName("foreign_count");
-  },
-
-  *check() {
-    Assert.equal((yield this._getForeignCount()), 2);
-  }
-});
-
-//------------------------------------------------------------------------------
-
-tests.push({
-  name: "L.5",
-  desc: "recalculate hashes when missing.",
-
-  *setup() {
-    this._pageGuid = (yield PlacesUtils.history.insert({ url: "http://l5.moz.org/",
-                                                         visits: [{ date: new Date() }] })).guid;
-    Assert.ok((yield this._getHash()) > 0);
-    yield PlacesUtils.withConnectionWrapper("change url hash", Task.async(function* (db) {
-      yield db.execute(`UPDATE moz_places SET url_hash = 0`);
-    }));
-    Assert.equal((yield this._getHash()), 0);
-  },
-
-  *_getHash() {
-    let db = yield PlacesUtils.promiseDBConnection();
-    let rows = yield db.execute(`SELECT url_hash FROM moz_places
-                                 WHERE guid = :guid`, { guid: this._pageGuid });
-    return rows[0].getResultByName("url_hash");
-  },
-
-  *check() {
-    Assert.ok((yield this._getHash()) > 0);
-  }
-});
-
-//------------------------------------------------------------------------------
-
-tests.push({
   name: "Z",
   desc: "Sanity: Preventive maintenance does not touch valid items",
 
   _uri1: uri("http://www1.mozilla.org"),
   _uri2: uri("http://www2.mozilla.org"),
   _folderId: null,
   _bookmarkId: null,
   _separatorId: null,
@@ -1313,18 +1256,29 @@ tests.push({
         resolve();
       });
     });
   })
 });
 
 //------------------------------------------------------------------------------
 
+// main
+function run_test()
+{
+  run_next_test();
+}
+
 add_task(function* test_preventive_maintenance()
 {
+  // Force initialization of the bookmarks hash. This test could cause
+  // it to go out of sync due to direct queries on the database.
+  yield PlacesTestUtils.addVisits(uri("http://force.bookmarks.hash"));
+  do_check_false(bs.isBookmarked(uri("http://force.bookmarks.hash")));
+
   // Get current bookmarks max ID for cleanup
   let stmt = mDBConn.createStatement("SELECT MAX(id) FROM moz_bookmarks");
   stmt.executeStep();
   defaultBookmarksMaxId = stmt.getInt32(0);
   stmt.finalize();
   do_check_true(defaultBookmarksMaxId > 0);
 
   for (let [, test] in Iterator(tests)) {
--- a/toolkit/modules/Sqlite.jsm
+++ b/toolkit/modules/Sqlite.jsm
@@ -926,20 +926,19 @@ function openConnection(options) {
         log.warn(`Could not open connection to ${path}: ${status}`);
         reject(new Error(`Could not open connection to ${path}: ${status}`));
         return;
       }
       log.info("Connection opened");
       try {
         resolve(
           new OpenedConnection(connection.QueryInterface(Ci.mozIStorageAsyncConnection),
-                               identifier, openedOptions));
+                              identifier, openedOptions));
       } catch (ex) {
         log.warn("Could not open database", ex);
-        connection.asyncClose();
         reject(ex);
       }
     });
   });
 }
 
 /**
  * Creates a clone of an existing and open Storage connection.  The clone has
@@ -1008,17 +1007,16 @@ function cloneStorageConnection(options)
         reject(new Error("Could not clone connection: " + status));
       }
       log.info("Connection cloned");
       try {
         let conn = connection.QueryInterface(Ci.mozIStorageAsyncConnection);
         resolve(new OpenedConnection(conn, identifier, openedOptions));
       } catch (ex) {
         log.warn("Could not clone database", ex);
-        connection.asyncClose();
         reject(ex);
       }
     });
   });
 }
 
 /**
  * Wraps an existing and open Storage connection with Sqlite.jsm API.  The