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 382389 cda8c83f6a5727cad1d971a6ccf0660aa4ea0fb7
parent 382388 3fa6d81f720a687d5b593e0f8d4eaed887724755
child 382390 3cd482f4d7fe1e0a40bb27ce3557fe10068154bd
child 382393 c38c875e31a14eb4c671b2b0884de2583ceb2901
push id21701
push userk.krish@yahoo.com
push dateWed, 29 Jun 2016 15:07:12 +0000
bugs889561
milestone50.0a1
backs outceff61c9fc5acc0f23ac589483d09444dea69ae7
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