Bug 889561 - Reduce the size of places.sqlite by removing the url unique index from moz_places. r=adw

MozReview-Commit-ID: 2kxaXnUYIwT

--HG--
extra : rebase_source : bbcaf85a0346b5347fc94053b4f193e4b89f2995
This commit is contained in:
Marco Bonardo 2016-06-29 14:47:36 +02:00
Родитель 2331b4c16c
Коммит 3837320431
51 изменённых файлов: 575 добавлений и 428 удалений

Просмотреть файл

@ -58,7 +58,8 @@ function* getTransitionForUrl(url) {
let rows = yield db.execute(`
SELECT visit_type
FROM moz_historyvisits
WHERE place_id = (SELECT id FROM moz_places WHERE url = :url)`,
JOIN moz_places h ON place_id = h.id
WHERE url_hash = hash(:url) AND url = :url`,
{ url });
if (rows.length) {
return rows[0].getResultByName("visit_type");

Просмотреть файл

@ -1094,7 +1094,7 @@ BookmarksStore.prototype = {
return this._getStmt(
"SELECT frecency " +
"FROM moz_places " +
"WHERE url = :url " +
"WHERE url_hash = hash(:url) AND url = :url " +
"LIMIT 1");
},
_frecencyCols: ["frecency"],

Просмотреть файл

@ -105,7 +105,7 @@ HistoryStore.prototype = {
return this._getStmt(
"UPDATE moz_places " +
"SET guid = :guid " +
"WHERE url = :page_url");
"WHERE url_hash = hash(:page_url) AND url = :page_url");
},
// Some helper functions to handle GUIDs
@ -127,7 +127,7 @@ HistoryStore.prototype = {
return this._getStmt(
"SELECT guid " +
"FROM moz_places " +
"WHERE url = :page_url");
"WHERE url_hash = hash(:page_url) AND url = :page_url");
},
_guidCols: ["guid"],
@ -146,12 +146,12 @@ HistoryStore.prototype = {
},
get _visitStm() {
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");
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`);
},
_visitCols: ["date", "type"],

Просмотреть файл

@ -24,7 +24,7 @@ add_task(function* test_ignore_invalid_uri() {
// 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
`UPDATE moz_places SET url = :url, url_hash = hash(:url)
WHERE id = (SELECT b.fk FROM moz_bookmarks b
WHERE b.id = :id LIMIT 1)`,
{ id: bmid, url: "<invalid url>" });

Просмотреть файл

@ -189,8 +189,8 @@ add_test(function test_invalid_records() {
.DBConnection;
let stmt = connection.createAsyncStatement(
"INSERT INTO moz_places "
+ "(url, title, rev_host, visit_count, last_visit_date) "
+ "VALUES ('invalid-uri', 'Invalid URI', '.', 1, " + TIMESTAMP3 + ")"
+ "(url, url_hash, title, rev_host, visit_count, last_visit_date) "
+ "VALUES ('invalid-uri', hash('invalid-uri'), 'Invalid URI', '.', 1, " + TIMESTAMP3 + ")"
);
Async.querySpinningly(stmt);
stmt.finalize();
@ -198,7 +198,7 @@ add_test(function test_invalid_records() {
stmt = connection.createAsyncStatement(
"INSERT INTO moz_historyvisits "
+ "(place_id, visit_date, visit_type, session) "
+ "VALUES ((SELECT id FROM moz_places WHERE url = 'invalid-uri'), "
+ "VALUES ((SELECT id FROM moz_places WHERE url_hash = hash('invalid-uri') AND url = 'invalid-uri'), "
+ TIMESTAMP3 + ", " + Ci.nsINavHistoryService.TRANSITION_TYPED + ", 1)"
);
Async.querySpinningly(stmt);

Просмотреть файл

@ -70,7 +70,7 @@ var HistoryEntry = {
"WHERE place_id = (" +
"SELECT id " +
"FROM moz_places " +
"WHERE url = :url) " +
"WHERE url_hash = hash(:url) AND url = :url) " +
"ORDER BY date DESC LIMIT 20");
this.__defineGetter__("_visitStm", () => stm);
return stm;

Просмотреть файл

@ -453,57 +453,6 @@ var Bookmarks = Object.freeze({
);
},
/**
* Searches a list of bookmark-items by a search term, url or title.
*
* @param query
* Either a string to use as search term, or an object
* containing any of these keys: query, title or url with the
* corresponding string to match as value.
* The url property can be either a string or an nsIURI.
*
* @return {Promise} resolved when the search is complete.
* @resolves to an array of found bookmark-items.
* @rejects if an error happens while searching.
* @throws if the arguments are invalid.
*
* @note Any unknown property in the query object is ignored.
* Known properties may be overwritten.
*/
search(query) {
if (!query) {
throw new Error("Query object is required");
}
if (typeof query === "string") {
query = { query: query };
}
if (typeof query !== "object") {
throw new Error("Query must be an object or a string");
}
if (query.query && typeof query.query !== "string") {
throw new Error("Query option must be a string");
}
if (query.title && typeof query.title !== "string") {
throw new Error("Title option must be a string");
}
if (query.url) {
if (typeof query.url === "string" || (query.url instanceof URL)) {
query.url = new URL(query.url).href;
} else if (query.url instanceof Ci.nsIURI) {
query.url = query.url.spec;
} else {
throw new Error("Url option must be a string or a URL object");
}
}
return Task.spawn(function* () {
let results = yield queryBookmarks(query);
return results;
});
},
/**
* Returns a list of recently bookmarked items.
*
@ -736,7 +685,64 @@ var Bookmarks = Object.freeze({
child.parentGuid ]);
}
}.bind(this));
}
},
/**
* Searches a list of bookmark-items by a search term, url or title.
*
* IMPORTANT:
* This is intended as an interim API for the web-extensions implementation.
* It will be removed as soon as we have a new querying API.
*
* If you just want to search bookmarks by URL, use .fetch() instead.
*
* @param query
* Either a string to use as search term, or an object
* containing any of these keys: query, title or url with the
* corresponding string to match as value.
* The url property can be either a string or an nsIURI.
*
* @return {Promise} resolved when the search is complete.
* @resolves to an array of found bookmark-items.
* @rejects if an error happens while searching.
* @throws if the arguments are invalid.
*
* @note Any unknown property in the query object is ignored.
* Known properties may be overwritten.
*/
search(query) {
if (!query) {
throw new Error("Query object is required");
}
if (typeof query === "string") {
query = { query: query };
}
if (typeof query !== "object") {
throw new Error("Query must be an object or a string");
}
if (query.query && typeof query.query !== "string") {
throw new Error("Query option must be a string");
}
if (query.title && typeof query.title !== "string") {
throw new Error("Title option must be a string");
}
if (query.url) {
if (typeof query.url === "string" || (query.url instanceof URL)) {
query.url = new URL(query.url).href;
} else if (query.url instanceof Ci.nsIURI) {
query.url = query.url.spec;
} else {
throw new Error("Url option must be a string or a URL object");
}
}
return Task.spawn(function* () {
let results = yield queryBookmarks(query);
return results;
});
},
});
////////////////////////////////////////////////////////////////////////////////
@ -776,14 +782,10 @@ function updateBookmark(info, item, newParent) {
yield db.executeTransaction(function* () {
if (info.hasOwnProperty("url")) {
// Ensure a page exists in moz_places for this 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: info.url ? info.url.href : null,
rev_host: PlacesUtils.getReversedHost(info.url),
frecency: info.url.protocol == "place:" ? 0 : -1 });
yield maybeInsertPlace(db, info.url);
// Update tuples for the update query.
tuples.set("url", { value: info.url.href
, fragment: "fk = (SELECT id FROM moz_places WHERE url = :url)" });
, fragment: "fk = (SELECT id FROM moz_places WHERE url_hash = hash(:url) AND url = :url)" });
}
if (newParent) {
@ -864,11 +866,8 @@ function insertBookmark(item, parent) {
yield db.executeTransaction(function* transaction() {
if (item.type == Bookmarks.TYPE_BOOKMARK) {
// Ensure a page exists in moz_places for this 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 });
// The IGNORE conflict can trigger on `guid`.
yield maybeInsertPlace(db, item.url);
}
// Adjust indices.
@ -882,7 +881,7 @@ function insertBookmark(item, parent) {
yield db.executeCached(
`INSERT INTO moz_bookmarks (fk, type, parent, position, title,
dateAdded, lastModified, guid)
VALUES ((SELECT id FROM moz_places WHERE url = :url), :type, :parent,
VALUES ((SELECT id FROM moz_places WHERE url_hash = hash(:url) AND 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,
@ -921,7 +920,7 @@ function queryBookmarks(info) {
}
if (info.url) {
queryString += " AND h.url = :url";
queryString += " AND h.url_hash = hash(:url) AND h.url = :url";
queryParams.url = info.url;
}
@ -1018,7 +1017,7 @@ function fetchBookmarksByURL(info) {
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 = :url
WHERE h.url_hash = hash(:url) AND h.url = :url
AND _grandParentId <> :tags_folder
ORDER BY b.lastModified DESC
`, { url: info.url.href,
@ -1398,17 +1397,18 @@ function validateBookmarkObject(input, behavior={}) {
* 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 IN ( ${urls.map(url => JSON.stringify(url.href)).join(", ")} )
) WHERE url_hash IN ( ${urls.map(url => `hash("${url.href}")`).join(", ")} )
`);
yield db.execute(
`UPDATE moz_places
SET hidden = 0
WHERE url IN ( ${urls.map(url => JSON.stringify(url.href)).join(", ")} )
WHERE url_hash IN ( ${urls.map(url => `hash(${JSON.stringify(url.href)})`).join(", ")} )
AND frecency <> 0
`);
});
@ -1564,3 +1564,21 @@ Task.async(function* (db, folderGuids) {
}
}
});
/**
* 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 });
}

Просмотреть файл

@ -847,6 +847,13 @@ Database::InitSchema(bool* aDatabaseMigrated)
// 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();
@ -861,7 +868,7 @@ Database::InitSchema(bool* aDatabaseMigrated)
// moz_places.
rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_PLACES);
NS_ENSURE_SUCCESS(rv, rv);
rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_URL);
rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_URL_HASH);
NS_ENSURE_SUCCESS(rv, rv);
rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_FAVICON);
NS_ENSURE_SUCCESS(rv, rv);
@ -1039,6 +1046,8 @@ Database::InitFunctions()
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;
}
@ -1771,6 +1780,39 @@ Database::MigrateV32Up() {
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()
{
@ -1802,7 +1844,7 @@ Database::Shutdown()
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 "
@ -1812,7 +1854,7 @@ Database::Shutdown()
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
@ -1828,7 +1870,31 @@ Database::Shutdown()
MOZ_ASSERT(NS_SUCCEEDED(rv));
rv = stmt->ExecuteStep(&hasUnroundedDates);
MOZ_ASSERT(NS_SUCCEEDED(rv));
MOZ_ASSERT(!hasUnroundedDates && "Found unrounded dates!");
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!");
}
#endif

Просмотреть файл

@ -18,7 +18,7 @@
// This is the schema version. Update it at any schema change and add a
// corresponding migrateVxx method below.
#define DATABASE_SCHEMA_VERSION 32
#define DATABASE_SCHEMA_VERSION 33
// Fired after Places inited.
#define TOPIC_PLACES_INIT_COMPLETE "places-init-complete"
@ -268,6 +268,7 @@ protected:
nsresult MigrateV30Up();
nsresult MigrateV31Up();
nsresult MigrateV32Up();
nsresult MigrateV33Up();
nsresult UpdateBookmarkRootTitles();

Просмотреть файл

@ -63,7 +63,7 @@ FetchPageInfo(const RefPtr<Database>& aDB,
"AND EXISTS(SELECT 1 FROM moz_bookmarks b WHERE b.fk = r_place_id) "
"LIMIT 1 "
") "
") FROM moz_places h WHERE h.url = :page_url",
") FROM moz_places h WHERE h.url_hash = hash(:page_url) AND h.url = :page_url",
nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY,
nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
@ -248,7 +248,7 @@ FetchIconURL(const RefPtr<Database>& aDB,
"SELECT f.url "
"FROM moz_places h "
"JOIN moz_favicons f ON h.favicon_id = f.id "
"WHERE h.url = :page_url"
"WHERE h.url_hash = hash(:page_url) AND h.url = :page_url"
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
@ -696,7 +696,8 @@ AsyncAssociateIconToPage::Run()
}
else {
stmt = DB->GetStatement(
"UPDATE moz_places SET favicon_id = :icon_id WHERE url = :page_url"
"UPDATE moz_places SET favicon_id = :icon_id "
"WHERE url_hash = hash(:page_url) AND url = :page_url"
);
NS_ENSURE_STATE(stmt);
rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), mPage.spec);

Просмотреть файл

@ -1957,7 +1957,7 @@ public:
if (!mIsVisitedStatement) {
(void)mReadOnlyDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
"SELECT 1 FROM moz_places h "
"WHERE url = ?1 AND last_visit_date NOTNULL "
"WHERE url_hash = hash(?1) AND url = ?1 AND last_visit_date NOTNULL "
), getter_AddRefs(mIsVisitedStatement));
MOZ_ASSERT(mIsVisitedStatement);
nsresult result = mIsVisitedStatement ? NS_OK : NS_ERROR_NOT_AVAILABLE;
@ -2037,8 +2037,8 @@ History::InsertPlace(VisitData& aPlace)
nsCOMPtr<mozIStorageStatement> stmt = GetStatement(
"INSERT INTO moz_places "
"(url, title, rev_host, hidden, typed, frecency, guid) "
"VALUES (:url, :title, :rev_host, :hidden, :typed, :frecency, :guid) "
"(url, url_hash, title, rev_host, hidden, typed, frecency, guid) "
"VALUES (:url, hash(:url), :title, :rev_host, :hidden, :typed, :frecency, :guid) "
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
@ -2147,7 +2147,7 @@ History::FetchPageInfo(VisitData& _place, bool* _exists)
"(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 = :page_url "
"WHERE url_hash = hash(:page_url) AND url = :page_url "
);
NS_ENSURE_STATE(stmt);

Просмотреть файл

@ -638,7 +638,8 @@ var clear = Task.async(function* (db) {
yield db.execute(
`UPDATE moz_places SET frecency =
(CASE
WHEN url BETWEEN 'place:' AND 'place;'
WHEN url_hash BETWEEN hash("place", "prefix_lo") AND
hash("place", "prefix_hi")
THEN 0
ELSE -1
END)
@ -862,10 +863,12 @@ var removeVisitsByFilter = Task.async(function*(db, filter, onResult = null) {
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 IN (${ sqlList(urls) })
`;
OR (url_hash IN (${ urls.map(u => "hash(" + JSON.stringify(u) + ")").join(",") })
AND url IN (${ sqlList(urls) }))
`;
let onResultData = onResult ? [] : null;
let pages = [];

Просмотреть файл

@ -718,9 +718,15 @@ this.PlacesDBUtils = {
// 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_bookmarks WHERE fk = moz_places.id ) +
(SELECT count(*) FROM moz_keywords WHERE place_id = 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;

Просмотреть файл

@ -1304,7 +1304,7 @@ this.PlacesUtils = {
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 = :url`;
WHERE h.url_hash = hash(:url) AND h.url = :url`;
let spec = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
yield conn.executeCached(QUERY_STR, { url: spec }, aRow => {
if (abort)
@ -1885,29 +1885,35 @@ XPCOMUtils.defineLazyGetter(this, "bundle", function() {
function setupDbForShutdown(conn, name) {
try {
let state = "0. Not started.";
let promiseClosed = new Promise(resolve => {
let promiseClosed = new Promise((resolve, reject) => {
// The service initiates shutdown.
// Before it can safely close its connection, we need to make sure
// that we have closed the high-level connection.
AsyncShutdown.placesClosingInternalConnection.addBlocker(`${name} closing as part of Places shutdown`,
Task.async(function*() {
state = "1. Service has initiated shutdown";
try {
AsyncShutdown.placesClosingInternalConnection.addBlocker(`${name} closing as part of Places shutdown`,
Task.async(function*() {
state = "1. Service has initiated shutdown";
// At this stage, all external clients have finished using the
// database. We just need to close the high-level connection.
yield conn.close();
state = "2. Closed Sqlite.jsm connection.";
// At this stage, all external clients have finished using the
// database. We just need to close the high-level connection.
yield conn.close();
state = "2. Closed Sqlite.jsm connection.";
resolve();
}),
() => state
);
resolve();
}),
() => state
);
} catch (ex) {
// It's too late to block shutdown, just close the connection.
conn.close();
reject(ex);
}
});
// Make sure that Sqlite.jsm doesn't close until we are done
// with the high-level connection.
Sqlite.shutdown.addBlocker(`${name} must be closed before Sqlite.jsm`,
() => promiseClosed,
() => promiseClosed.catch(Cu.reportError),
() => state
);
} catch(ex) {
@ -1924,7 +1930,7 @@ XPCOMUtils.defineLazyGetter(this, "gAsyncDBConnPromised",
}).then(conn => {
setupDbForShutdown(conn, "PlacesUtils read-only connection");
return conn;
})
}).catch(Cu.reportError)
);
XPCOMUtils.defineLazyGetter(this, "gAsyncDBWrapperPromised",
@ -1933,7 +1939,7 @@ XPCOMUtils.defineLazyGetter(this, "gAsyncDBWrapperPromised",
}).then(conn => {
setupDbForShutdown(conn, "PlacesUtils wrapped connection");
return conn;
})
}).catch(Cu.reportError)
);
/**
@ -2061,22 +2067,24 @@ var Keywords = {
if (oldEntry) {
yield db.executeCached(
`UPDATE moz_keywords
SET place_id = (SELECT id FROM moz_places WHERE url = :url),
SET place_id = (SELECT id FROM moz_places WHERE url_hash = hash(:url) AND 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.
// create it. The IGNORE conflict can trigger on `guid`.
yield db.executeCached(
`INSERT OR IGNORE INTO moz_places (url, rev_host, hidden, frecency, guid)
VALUES (:url, :rev_host, 0, :frecency, GENERATE_GUID())
`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 });
yield db.executeCached(
`INSERT INTO moz_keywords (keyword, place_id, post_data)
VALUES (:keyword, (SELECT id FROM moz_places WHERE url = :url), :post_data)
VALUES (:keyword, (SELECT id FROM moz_places WHERE url_hash = hash(:url) AND url = :url), :post_data)
`, { url: url.href, keyword: keyword, post_data: postData });
}

Просмотреть файл

@ -17,6 +17,7 @@
#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.
@ -183,13 +184,6 @@ namespace places {
////////////////////////////////////////////////////////////////////////////////
//// AutoComplete Matching Function
//////////////////////////////////////////////////////////////////////////////
//// MatchAutoCompleteFunction
MatchAutoCompleteFunction::~MatchAutoCompleteFunction()
{
}
/* static */
nsresult
MatchAutoCompleteFunction::create(mozIStorageConnection *aDBConn)
@ -328,9 +322,6 @@ namespace places {
mozIStorageFunction
)
//////////////////////////////////////////////////////////////////////////////
//// mozIStorageFunction
NS_IMETHODIMP
MatchAutoCompleteFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
nsIVariant **_result)
@ -438,13 +429,6 @@ namespace places {
////////////////////////////////////////////////////////////////////////////////
//// Frecency Calculation Function
//////////////////////////////////////////////////////////////////////////////
//// CalculateFrecencyFunction
CalculateFrecencyFunction::~CalculateFrecencyFunction()
{
}
/* static */
nsresult
CalculateFrecencyFunction::create(mozIStorageConnection *aDBConn)
@ -465,9 +449,6 @@ namespace places {
mozIStorageFunction
)
//////////////////////////////////////////////////////////////////////////////
//// mozIStorageFunction
NS_IMETHODIMP
CalculateFrecencyFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
nsIVariant **_result)
@ -628,13 +609,6 @@ namespace places {
////////////////////////////////////////////////////////////////////////////////
//// GUID Creation Function
GenerateGUIDFunction::~GenerateGUIDFunction()
{
}
//////////////////////////////////////////////////////////////////////////////
//// GenerateGUIDFunction
/* static */
nsresult
GenerateGUIDFunction::create(mozIStorageConnection *aDBConn)
@ -653,9 +627,6 @@ namespace places {
mozIStorageFunction
)
//////////////////////////////////////////////////////////////////////////////
//// mozIStorageFunction
NS_IMETHODIMP
GenerateGUIDFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
nsIVariant **_result)
@ -671,13 +642,6 @@ namespace places {
////////////////////////////////////////////////////////////////////////////////
//// Get Unreversed Host Function
GetUnreversedHostFunction::~GetUnreversedHostFunction()
{
}
//////////////////////////////////////////////////////////////////////////////
//// GetUnreversedHostFunction
/* static */
nsresult
GetUnreversedHostFunction::create(mozIStorageConnection *aDBConn)
@ -696,9 +660,6 @@ namespace places {
mozIStorageFunction
)
//////////////////////////////////////////////////////////////////////////////
//// mozIStorageFunction
NS_IMETHODIMP
GetUnreversedHostFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
nsIVariant **_result)
@ -727,13 +688,6 @@ namespace places {
////////////////////////////////////////////////////////////////////////////////
//// Fixup URL Function
FixupURLFunction::~FixupURLFunction()
{
}
//////////////////////////////////////////////////////////////////////////////
//// FixupURLFunction
/* static */
nsresult
FixupURLFunction::create(mozIStorageConnection *aDBConn)
@ -752,9 +706,6 @@ namespace places {
mozIStorageFunction
)
//////////////////////////////////////////////////////////////////////////////
//// mozIStorageFunction
NS_IMETHODIMP
FixupURLFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
nsIVariant **_result)
@ -787,10 +738,6 @@ namespace places {
////////////////////////////////////////////////////////////////////////////////
//// Frecency Changed Notification Function
FrecencyNotificationFunction::~FrecencyNotificationFunction()
{
}
/* static */
nsresult
FrecencyNotificationFunction::create(mozIStorageConnection *aDBConn)
@ -847,10 +794,6 @@ namespace places {
////////////////////////////////////////////////////////////////////////////////
//// Store Last Inserted Id Function
StoreLastInsertedIdFunction::~StoreLastInsertedIdFunction()
{
}
/* static */
nsresult
StoreLastInsertedIdFunction::create(mozIStorageConnection *aDBConn)
@ -894,5 +837,83 @@ namespace places {
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

Просмотреть файл

@ -71,7 +71,7 @@ public:
static nsresult create(mozIStorageConnection *aDBConn);
private:
~MatchAutoCompleteFunction();
~MatchAutoCompleteFunction() {}
/**
* Argument Indexes
@ -174,7 +174,6 @@ private:
};
////////////////////////////////////////////////////////////////////////////////
//// Frecency Calculation Function
@ -197,7 +196,6 @@ private:
*/
class CalculateFrecencyFunction final : public mozIStorageFunction
{
~CalculateFrecencyFunction();
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_MOZISTORAGEFUNCTION
@ -209,6 +207,8 @@ public:
* The database connection to register with.
*/
static nsresult create(mozIStorageConnection *aDBConn);
private:
~CalculateFrecencyFunction() {}
};
/**
@ -219,7 +219,6 @@ public:
*/
class GenerateGUIDFunction final : public mozIStorageFunction
{
~GenerateGUIDFunction();
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_MOZISTORAGEFUNCTION
@ -231,6 +230,8 @@ public:
* The database connection to register with.
*/
static nsresult create(mozIStorageConnection *aDBConn);
private:
~GenerateGUIDFunction() {}
};
/**
@ -243,7 +244,6 @@ public:
*/
class GetUnreversedHostFunction final : public mozIStorageFunction
{
~GetUnreversedHostFunction();
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_MOZISTORAGEFUNCTION
@ -255,6 +255,8 @@ public:
* The database connection to register with.
*/
static nsresult create(mozIStorageConnection *aDBConn);
private:
~GetUnreversedHostFunction() {}
};
@ -272,7 +274,6 @@ public:
*/
class FixupURLFunction final : public mozIStorageFunction
{
~FixupURLFunction();
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_MOZISTORAGEFUNCTION
@ -284,6 +285,8 @@ public:
* The database connection to register with.
*/
static nsresult create(mozIStorageConnection *aDBConn);
private:
~FixupURLFunction() {}
};
@ -309,7 +312,6 @@ public:
*/
class FrecencyNotificationFunction final : public mozIStorageFunction
{
~FrecencyNotificationFunction();
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_MOZISTORAGEFUNCTION
@ -321,6 +323,8 @@ public:
* The database connection to register with.
*/
static nsresult create(mozIStorageConnection *aDBConn);
private:
~FrecencyNotificationFunction() {}
};
@ -338,7 +342,6 @@ public:
*/
class StoreLastInsertedIdFunction final : public mozIStorageFunction
{
~StoreLastInsertedIdFunction();
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_MOZISTORAGEFUNCTION
@ -350,6 +353,37 @@ public:
* 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

Просмотреть файл

@ -132,7 +132,7 @@ 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 = t.url
LEFT JOIN moz_places h ON h.url_hash = hash(t.url) AND h.url = t.url
WHERE h.id IS NULL
AND AUTOCOMPLETE_MATCH(:searchString, t.url, t.url, NULL,
NULL, NULL, NULL, t.open_count,

Просмотреть файл

@ -1260,7 +1260,7 @@ nsAnnotationService::GetAnnotationNamesTArray(nsIURI* aURI,
"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 = :page_url"
"WHERE h.url_hash = hash(:page_url) AND h.url = :page_url"
);
}
NS_ENSURE_STATE(statement);
@ -1382,7 +1382,8 @@ nsAnnotationService::RemoveAnnotationInternal(nsIURI* aURI,
else {
statement = mDB->GetStatement(
"DELETE FROM moz_annos "
"WHERE place_id = (SELECT id FROM moz_places WHERE url = :page_url) "
"WHERE place_id = "
"(SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url) "
"AND anno_attribute_id = "
"(SELECT id FROM moz_anno_attributes WHERE name = :anno_name)"
);
@ -1445,7 +1446,7 @@ nsAnnotationService::RemovePageAnnotations(nsIURI* 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 = :page_url)"
"(SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url)"
);
NS_ENSURE_STATE(statement);
mozStorageStatementScoper scoper(statement);
@ -1508,7 +1509,7 @@ nsAnnotationService::CopyPageAnnotations(nsIURI* aSourceURI,
"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 = :dest_url) "
"(SELECT id FROM moz_places WHERE url_hash = hash(:dest_url) AND url = :dest_url) "
"AND a2.anno_attribute_id = n.id "
"WHERE url = :source_url"
);
@ -1524,7 +1525,7 @@ nsAnnotationService::CopyPageAnnotations(nsIURI* aSourceURI,
"INSERT INTO moz_annos "
"(place_id, anno_attribute_id, content, flags, expiration, "
"type, dateAdded, lastModified) "
"SELECT (SELECT id FROM moz_places WHERE url = :page_url), "
"SELECT (SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url), "
"anno_attribute_id, content, flags, expiration, type, "
":date, :date "
"FROM moz_annos "
@ -1703,7 +1704,7 @@ nsAnnotationService::HasAnnotationInternal(nsIURI* aURI,
"FROM moz_places h "
"LEFT JOIN moz_annos a ON a.place_id = h.id "
"AND a.anno_attribute_id = nameid "
"WHERE h.url = :page_url"
"WHERE h.url_hash = hash(:page_url) AND h.url = :page_url"
);
}
NS_ENSURE_STATE(stmt);
@ -1770,7 +1771,7 @@ nsAnnotationService::StartGetAnnotation(nsIURI* aURI,
"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 = :page_url "
"WHERE h.url_hash = hash(:page_url) AND h.url = :page_url "
"AND n.name = :anno_name"
);
}
@ -1864,7 +1865,7 @@ nsAnnotationService::StartSetAnnotation(nsIURI* aURI,
"FROM moz_places h "
"LEFT JOIN moz_annos a ON a.place_id = h.id "
"AND a.anno_attribute_id = nameid "
"WHERE h.url = :page_url"
"WHERE h.url_hash = hash(:page_url) AND h.url = :page_url"
);
}
NS_ENSURE_STATE(stmt);

Просмотреть файл

@ -60,7 +60,7 @@ public:
"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 = :page_url) "
"WHERE b.fk = (SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url) "
"ORDER BY b.lastModified DESC, b.id DESC "
);
if (stmt) {
@ -1853,7 +1853,7 @@ nsNavBookmarks::IsBookmarked(nsIURI* aURI, bool* aBookmarked)
nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
"SELECT 1 FROM moz_bookmarks b "
"JOIN moz_places h ON b.fk = h.id "
"WHERE h.url = :page_url"
"WHERE h.url_hash = hash(:page_url) AND h.url = :page_url"
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
@ -2059,7 +2059,7 @@ nsNavBookmarks::GetBookmarkIdsForURITArray(nsIURI* aURI,
"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 = :page_url) "
"WHERE b.fk = (SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url) "
"ORDER BY b.lastModified DESC, b.id DESC "
);
NS_ENSURE_STATE(stmt);
@ -2103,7 +2103,7 @@ nsNavBookmarks::GetBookmarksForURI(nsIURI* aURI,
"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 = :page_url) "
"WHERE b.fk = (SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url) "
"ORDER BY b.lastModified DESC, b.id DESC "
);
NS_ENSURE_STATE(stmt);

Просмотреть файл

@ -369,7 +369,7 @@ nsNavHistory::GetIdForPage(nsIURI* aURI,
nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
"SELECT id, url, title, rev_host, visit_count, guid "
"FROM moz_places "
"WHERE url = :page_url "
"WHERE url_hash = hash(:page_url) AND url = :page_url "
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
@ -405,8 +405,8 @@ nsNavHistory::GetOrCreateIdForPage(nsIURI* aURI,
// Create a new hidden, untyped and unvisited entry.
nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
"INSERT INTO moz_places (url, rev_host, hidden, frecency, guid) "
"VALUES (:page_url, :rev_host, :hidden, :frecency, :guid) "
"INSERT INTO moz_places (url, url_hash, rev_host, hidden, frecency, guid) "
"VALUES (:page_url, hash(:page_url), :rev_host, :hidden, :frecency, :guid) "
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
@ -1029,7 +1029,8 @@ nsNavHistory::invalidateFrecencies(const nsCString& aPlaceIdsQueryString)
invalidFrecenciesSQLFragment.AppendLiteral("NOTIFY_FRECENCY(");
invalidFrecenciesSQLFragment.AppendLiteral(
"(CASE "
"WHEN url BETWEEN 'place:' AND 'place;' "
"WHEN url_hash BETWEEN hash('place', 'prefix_lo') AND "
"hash('place', 'prefix_hi') "
"THEN 0 "
"ELSE -1 "
"END) "
@ -1833,7 +1834,8 @@ PlacesSQLQueryBuilder::SelectAsSite()
"%s "
"WHERE h.hidden = 0 "
"AND h.visit_count > 0 "
"AND h.url BETWEEN 'file://' AND 'file:/~' "
"AND h.url_hash BETWEEN hash('file', 'prefix_lo') AND "
"hash('file', 'prefix_hi') "
"%s "
"LIMIT 1 "
") "
@ -2901,7 +2903,7 @@ nsNavHistory::GetPageTitle(nsIURI* aURI, nsAString& aTitle)
nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
"SELECT id, url, title, rev_host, visit_count, guid "
"FROM moz_places "
"WHERE url = :page_url "
"WHERE url_hash = hash(:page_url) AND url = :page_url "
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
@ -3447,7 +3449,8 @@ nsNavHistory::QueryToSelectClause(nsNavHistoryQuery* aQuery, // const
if (excludeQueries) {
// Serching by terms implicitly exclude queries.
clause.Condition("NOT h.url BETWEEN 'place:' AND 'place;'");
clause.Condition("NOT h.url_hash BETWEEN hash('place', 'prefix_lo') AND "
"hash('place', 'prefix_hi')");
}
clause.GetClauseString(*aClause);
@ -4201,7 +4204,7 @@ nsNavHistory::URIToResultNode(nsIURI* aURI,
"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 = :page_url ")
"WHERE h.url_hash = hash(:page_url) AND h.url = :page_url ")
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
@ -4536,7 +4539,7 @@ nsNavHistory::AutoCompleteFeedback(int32_t aIndex,
"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 = :page_url "
"WHERE url_hash = hash(:page_url) AND url = :page_url "
);
NS_ENSURE_STATE(stmt);

Просмотреть файл

@ -191,7 +191,7 @@ nsNavHistoryResultNode::GetTags(nsAString& aTags) {
"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 = :page_url) "
"WHERE b.fk = (SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url) "
"AND t.parent = :tags_folder "
"ORDER BY t.title COLLATE NOCASE ASC "
") "

Просмотреть файл

@ -384,7 +384,7 @@ function nsPlacesAutoComplete()
`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 = t.url
LEFT JOIN moz_places h ON h.url_hash = hash(t.url) AND h.url = t.url
WHERE h.id IS NULL
AND AUTOCOMPLETE_MATCH(:searchString, t.url, t.url, NULL,
NULL, NULL, NULL, t.open_count,

Просмотреть файл

@ -26,6 +26,7 @@ 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");
@ -41,6 +42,8 @@ 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.";
@ -72,7 +75,7 @@ const DATABASE_TO_MEMORY_PERC = 4;
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 = 167772160; // 160MiB
const DATABASE_MAX_SIZE = 62914560; // 60MiB
// 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.
@ -100,9 +103,8 @@ 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.
// Based on these values we evaluate how many unique URIs we can handle before
// starting expiring some.
const URIENTRY_AVG_SIZE = 1600;
// Used as a fall back value when it's not possible to calculate the real value.
const URIENTRY_AVG_SIZE = 600;
// 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
@ -482,9 +484,6 @@ function nsPlacesExpiration()
return db;
});
XPCOMUtils.defineLazyServiceGetter(this, "_sys",
"@mozilla.org/system-info;1",
"nsIPropertyBag2");
XPCOMUtils.defineLazyServiceGetter(this, "_idle",
"@mozilla.org/widget/idleservice;1",
"nsIIdleService");
@ -492,18 +491,19 @@ function nsPlacesExpiration()
this._prefBranch = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefService).
getBranch(PREF_BRANCH);
this._loadPrefs();
// Observe our preferences branch for changes.
this._prefBranch.addObserver("", this, false);
this._loadPrefs().then(() => {
// Observe our preferences branch for changes.
this._prefBranch.addObserver("", this, true);
// Create our expiration timer.
this._newTimer();
}, Cu.reportError);
// Register topic observers.
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();
Services.obs.addObserver(this, TOPIC_SHUTDOWN, true);
Services.obs.addObserver(this, TOPIC_DEBUG_START_EXPIRATION, true);
Services.obs.addObserver(this, TOPIC_IDLE_DAILY, true);
}
nsPlacesExpiration.prototype = {
@ -513,14 +513,12 @@ nsPlacesExpiration.prototype = {
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) {
@ -540,12 +538,12 @@ nsPlacesExpiration.prototype = {
this._finalizeInternalStatements();
}
else if (aTopic == TOPIC_PREF_CHANGED) {
this._loadPrefs();
if (aData == PREF_INTERVAL_SECONDS) {
// Renew the timer with the new interval value.
this._newTimer();
}
this._loadPrefs().then(() => {
if (aData == PREF_INTERVAL_SECONDS) {
// Renew the timer with the new interval value.
this._newTimer();
}
}, Cu.reportError);
}
else if (aTopic == TOPIC_DEBUG_START_EXPIRATION) {
// The passed-in limit is the maximum number of visits to expire when
@ -590,6 +588,9 @@ nsPlacesExpiration.prototype = {
else if (aTopic == TOPIC_IDLE_DAILY) {
this._expireWithActionAndLimit(ACTION.IDLE_DAILY, LIMIT.LARGE);
}
else if (aTopic == TOPIC_TESTING_MODE) {
this._testingMode = true;
}
},
//////////////////////////////////////////////////////////////////////////////
@ -817,25 +818,27 @@ nsPlacesExpiration.prototype = {
return this._expireOnIdle;
},
_loadPrefs: function PEX__loadPrefs() {
_loadPrefs: Task.async(function* () {
// 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(e) {}
} catch(ex) { /* User limit not set */ }
if (this._urisLimit < 0) {
// 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.
// 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 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 = this._sys.getProperty("memsize");
memSizeBytes = Services.sysinfo.getProperty("memsize");
} catch (ex) {}
if (memSizeBytes <= 0) {
memsize = MEMSIZE_FALLBACK_BYTES;
@ -858,7 +861,20 @@ nsPlacesExpiration.prototype = {
DATABASE_MAX_SIZE
);
this._urisLimit = Math.ceil(optimalDatabaseSize / URIENTRY_AVG_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);
}
// Expose the calculated limit to other components.
@ -870,11 +886,11 @@ nsPlacesExpiration.prototype = {
// 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 (e) {}
if (this._interval <= 0)
} catch (ex) { /* User interval not set */ }
if (this._interval <= 0) {
this._interval = PREF_INTERVAL_SECONDS_NOTSET;
},
}
}),
/**
* Evaluates the real number of pages in the database and the value currently
@ -1069,6 +1085,10 @@ nsPlacesExpiration.prototype = {
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;
},
@ -1084,6 +1104,7 @@ nsPlacesExpiration.prototype = {
, Ci.nsINavHistoryObserver
, Ci.nsITimerCallback
, Ci.mozIStorageStatementCallback
, Ci.nsISupportsWeakReference
])
};

Просмотреть файл

@ -16,9 +16,9 @@
/**
* moz_places
*/
#define CREATE_IDX_MOZ_PLACES_URL \
#define CREATE_IDX_MOZ_PLACES_URL_HASH \
CREATE_PLACES_IDX( \
"url_uniqueindex", "moz_places", "url", "UNIQUE" \
"url_hashindex", "moz_places", "url_hash", "" \
)
#define CREATE_IDX_MOZ_PLACES_FAVICON \

Просмотреть файл

@ -22,6 +22,7 @@
", last_visit_date INTEGER " \
", guid TEXT" \
", foreign_count INTEGER DEFAULT 0 NOT NULL" \
", url_hash INTEGER DEFAULT 0 NOT NULL " \
")" \
)

Просмотреть файл

@ -66,7 +66,7 @@ TaggingService.prototype = {
let stmt = db.createStatement(
`SELECT id FROM moz_bookmarks
WHERE parent = :tag_id
AND fk = (SELECT id FROM moz_places WHERE url = :page_url)`
AND fk = (SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url)`
);
stmt.params.tag_id = tagId;
stmt.params.page_url = aURI.spec;
@ -380,7 +380,7 @@ TaggingService.prototype = {
let stmt = db.createStatement(
`SELECT id, parent
FROM moz_bookmarks
WHERE fk = (SELECT id FROM moz_places WHERE url = :page_url)`
WHERE fk = (SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url)`
);
stmt.params.page_url = aURI.spec;
try {

Просмотреть файл

@ -64,7 +64,7 @@ this.PlacesTestUtils = Object.freeze({
}
if (typeof place.referrer == "string") {
place.referrer = NetUtil.newURI(place.referrer);
} else if (place.referrer instanceof URL) {
} else if (place.referrer && place.referrer instanceof URL) {
place.referrer = NetUtil.newURI(place.referrer.href);
}
place.visits = [{
@ -149,7 +149,7 @@ this.PlacesTestUtils = Object.freeze({
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 = :url",
"SELECT id FROM moz_places WHERE url_hash = hash(:url) AND url = :url",
{ url });
return rows.length > 0;
}),
@ -169,7 +169,7 @@ this.PlacesTestUtils = Object.freeze({
let rows = yield db.executeCached(
`SELECT count(*) FROM moz_historyvisits v
JOIN moz_places h ON h.id = v.place_id
WHERE url = :url`,
WHERE url_hash = hash(:url) AND url = :url`,
{ url });
return rows[0].getResultByIndex(0);
})

Просмотреть файл

@ -15,7 +15,8 @@ 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 = :t_url ", { t_url: url });
`SELECT foreign_count FROM moz_places WHERE url_hash = hash(:t_url)
AND url = :t_url`, { t_url: url });
return rows[0].getResultByName("foreign_count");
}
@ -68,7 +69,8 @@ add_task(function* maintenance_foreign_count_test() {
// 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 = :t_url ");
`UPDATE moz_places SET foreign_count = 10 WHERE url_hash = hash(:t_url)
AND url = :t_url `);
stmt.params.t_url = T_URI.spec;
stmt.executeAsync({
handleCompletion: function(){

Просмотреть файл

@ -120,34 +120,13 @@ function* checkHistoryItems() {
let visitedUri = visitedURIs[i];
ok((yield promiseIsURIVisited(visitedUri)), "");
if (/embed/.test(visitedUri.spec)) {
is(!!pageInDatabase(visitedUri), false, "Check if URI is in database");
is((yield PlacesTestUtils.isPageInDB(visitedUri)), false, "Check if URI is in database");
} else {
ok(!!pageInDatabase(visitedUri), "Check if URI is in database");
ok((yield PlacesTestUtils.isPageInDB(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

Просмотреть файл

@ -34,7 +34,7 @@ add_task(function* () {
WHERE from_visit IN
(SELECT v.id FROM moz_historyvisits v
JOIN moz_places p ON p.id = v.place_id
WHERE p.url = :url)
WHERE p.url_hash = hash(:url) AND p.url = :url)
`, { url: TEST_URI.spec });
is(rows.length, 1, "Found right number of visits");

Просмотреть файл

@ -18,11 +18,6 @@ add_task(function* () {
});
let visited = yield promiseIsURIVisited(SJS_URI);
ok(!visited, "The POST page should not be added to history");
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();
ok(!(yield PlacesTestUtils.isPageInDB(SJS_URI.spec)), "The page should not be in the database");
}));
});

Просмотреть файл

@ -1,20 +1,14 @@
/**
* 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, fromColumnName, fromColumnValue)
function getColumn(table, column, url)
{
var stmt = conn.createStatement(
`SELECT ${column} FROM ${table} WHERE ${fromColumnName} = :val
LIMIT 1`);
`SELECT ${column} FROM ${table} WHERE url_hash = hash(:val) AND url = :val`);
try {
stmt.params.val = fromColumnValue;
stmt.params.val = url;
stmt.executeStep();
return stmt.row[column];
}
@ -69,10 +63,10 @@ add_task(function* ()
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", "url", data[0].uri.spec));
is(data[0].guid, getColumn("moz_places", "guid", data[0].uri.spec));
data.forEach(function(item) {
var title = getColumn("moz_places", "title", "url", data[0].uri.spec);
var title = getColumn("moz_places", "title", data[0].uri.spec);
is(title, item.title);
});

Просмотреть файл

@ -1,8 +1,3 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/**
* One-time observer callback.
*/
@ -26,6 +21,7 @@ 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 {

Просмотреть файл

@ -28,7 +28,7 @@ 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 = :page_url`
`SELECT ${aFieldName} FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url`
);
stmt.params.page_url = url;
stmt.executeAsync({
@ -216,7 +216,7 @@ function doGetGuidForURI(aURI) {
let stmt = DBConn().createStatement(
`SELECT guid
FROM moz_places
WHERE url = :url`
WHERE url_hash = hash(:url) AND url = :url`
);
stmt.params.url = aURI.spec;
ok(stmt.executeStep(), "Check get guid for uri from moz_places");

Просмотреть файл

@ -269,7 +269,7 @@ do_get_place(nsIURI* aURI, PlaceRecord& result)
rv = dbConn->CreateStatement(NS_LITERAL_CSTRING(
"SELECT id, hidden, typed, visit_count, guid FROM moz_places "
"WHERE url=?1 "
"WHERE url_hash = hash(?1) AND url = ?1"
), getter_AddRefs(stmt));
do_check_success(rv);

Просмотреть файл

@ -34,7 +34,9 @@ function shutdownExpiration()
* history notification.
*/
function force_expiration_start() {
Cc["@mozilla.org/places/expiration;1"].getService(Ci.nsISupports);
Cc["@mozilla.org/places/expiration;1"]
.getService(Ci.nsIObserver)
.observe(null, "testing-mode", null);
}

Просмотреть файл

@ -52,7 +52,7 @@ function add_old_anno(aIdentifier, aName, aValue, aExpirePolicy,
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 = :id " +
"WHERE h.url_hash = hash(:id) AND h.url = :id " +
"ORDER BY a.dateAdded DESC LIMIT 1)";
}
else

Просмотреть файл

@ -55,7 +55,7 @@ function add_old_anno(aIdentifier, aName, aValue, aExpirePolicy,
"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 = :id " +
"WHERE h.url_hash = hash(:id) AND h.url = :id " +
"AND n.name = :anno_name " +
"ORDER BY a.dateAdded DESC LIMIT 1 " +
")";

Просмотреть файл

@ -52,7 +52,7 @@ function run_test() {
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_uniqueindex')"
+ "WHERE idx = 'moz_places_url_hashindex')"
);
stmt.executeAsync({
handleResult: function(aResultSet) {

Просмотреть файл

@ -1,9 +1,3 @@
/* -*- 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:
*
@ -21,46 +15,6 @@ 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
@ -89,32 +43,21 @@ var tests = [
var currentTest;
function run_test() {
add_task(function* test() {
// The pref should not exist by default.
try {
getInterval();
do_throw("interval pref should not exist by default");
}
catch (ex) {}
// Use our own mock timer implementation.
replace_timer_factory();
Assert.throws(() => getInterval());
// Force the component, so it will start observing preferences.
force_expiration_start();
runNextTest();
do_test_pending();
}
function runNextTest() {
if (tests.length) {
currentTest = tests.shift();
for (let currentTest of tests) {
print(currentTest.desc);
let promise = promiseTopicObserved("test-interval-changed");
setInterval(currentTest.interval);
let [, data] = yield promise;
Assert.equal(data, currentTest.expectedTimerDelay * EXPIRE_AGGRESSIVITY_MULTIPLIER);
}
else {
clearInterval();
do_test_finished();
}
}
clearInterval();
});

Просмотреть файл

@ -88,7 +88,7 @@ add_task(function* test_pref_maxpages() {
}
// Observe history.
historyObserver = {
let historyObserver = {
onBeginUpdateBatch: function PEX_onBeginUpdateBatch() {},
onEndUpdateBatch: function PEX_onEndUpdateBatch() {},
onClearHistory: function() {},

Просмотреть файл

@ -18,6 +18,4 @@ skip-if = os == "android"
[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]

Просмотреть файл

@ -3,7 +3,7 @@
* 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 = 32;
const CURRENT_SCHEMA_VERSION = 33;
const FIRST_UPGRADABLE_SCHEMA_VERSION = 11;
const NS_APP_USER_PROFILE_50_DIR = "ProfD";
@ -299,7 +299,7 @@ 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 = :url"
"SELECT id FROM moz_places WHERE url_hash = hash(:url) AND url = :url"
);
stmt.params.url = url;
try {
@ -324,7 +324,7 @@ function visits_in_database(aURI)
let stmt = DBConn().createStatement(
`SELECT count(*) FROM moz_historyvisits v
JOIN moz_places h ON h.id = v.place_id
WHERE url = :url`
WHERE url_hash = hash(:url) AND url = :url`
);
stmt.params.url = url;
try {
@ -542,7 +542,7 @@ function frecencyForUrl(aURI)
url = aURI.href;
}
let stmt = DBConn().createStatement(
"SELECT frecency FROM moz_places WHERE url = ?1"
"SELECT frecency FROM moz_places WHERE url_hash = hash(?1) AND url = ?1"
);
stmt.bindByIndex(0, url);
try {
@ -566,7 +566,7 @@ function isUrlHidden(aURI)
{
let url = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
let stmt = DBConn().createStatement(
"SELECT hidden FROM moz_places WHERE url = ?1"
"SELECT hidden FROM moz_places WHERE url_hash = hash(?1) AND url = ?1"
);
stmt.bindByIndex(0, url);
if (!stmt.executeStep())
@ -643,7 +643,7 @@ function do_get_guid_for_uri(aURI,
let stmt = DBConn().createStatement(
`SELECT guid
FROM moz_places
WHERE url = :url`
WHERE url_hash = hash(:url) AND url = :url`
);
stmt.params.url = aURI.spec;
do_check_true(stmt.executeStep(), aStack);
@ -866,7 +866,7 @@ function* foreign_count(url) {
let db = yield PlacesUtils.promiseDBConnection();
let rows = yield db.executeCached(
`SELECT foreign_count FROM moz_places
WHERE url = :url
WHERE url_hash = hash(:url) AND url = :url
`, { url });
return rows.length == 0 ? 0 : rows[0].getResultByName("foreign_count");
}

Двоичные данные
toolkit/components/places/tests/migration/places_v33.sqlite Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -19,6 +19,7 @@ support-files =
places_v30.sqlite
places_v31.sqlite
places_v32.sqlite
places_v33.sqlite
[test_current_from_downgraded.js]
[test_current_from_v6.js]

Просмотреть файл

@ -60,7 +60,7 @@ function* task_populateDB(aArray)
// 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 = :url");
"UPDATE moz_places SET visit_count = :vc WHERE url_hash = hash(:url) AND url = :url");
stmt.params.vc = qdata.visitCount;
stmt.params.url = qdata.uri;
try {
@ -79,7 +79,7 @@ function* task_populateDB(aArray)
// 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 = :url");
"UPDATE moz_places SET hidden = 1 WHERE url_hash = hash(:url) AND url = :url");
stmt.params.url = qdata.uri;
try {
stmt.executeAsync();

Просмотреть файл

@ -1,6 +1,3 @@
/* 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.
*/
@ -131,7 +128,7 @@ function do_check_title_for_uri(aURI,
let stmt = DBConn().createStatement(
`SELECT title
FROM moz_places
WHERE url = :url`
WHERE url_hash = hash(:url) AND url = :url`
);
stmt.params.url = aURI.spec;
do_check_true(stmt.executeStep(), stack);
@ -548,7 +545,8 @@ add_task(function* test_old_referrer_ignored() {
let stmt = DBConn().createStatement(
`SELECT COUNT(1) AS count
FROM moz_historyvisits
WHERE place_id = (SELECT id FROM moz_places WHERE url = :page_url)
JOIN moz_places h ON h.id = place_id
WHERE url_hash = hash(:page_url) AND url = :page_url
AND from_visit = 0`
);
stmt.params.page_url = place.uri.spec;
@ -740,7 +738,7 @@ add_task(function* test_properties_saved() {
FROM moz_places h
JOIN moz_historyvisits v
ON h.id = v.place_id
WHERE h.url = :page_url
WHERE h.url_hash = hash(:page_url) AND h.url = :page_url
AND v.visit_date = :visit_date`
);
stmt.params.page_url = uri.spec;
@ -755,7 +753,7 @@ add_task(function* test_properties_saved() {
FROM moz_places h
JOIN moz_historyvisits v
ON h.id = v.place_id
WHERE h.url = :page_url
WHERE h.url_hash = hash(:page_url) AND h.url = :page_url
AND v.visit_type = :transition_type`
);
stmt.params.page_url = uri.spec;
@ -768,7 +766,7 @@ add_task(function* test_properties_saved() {
stmt = DBConn().createStatement(
`SELECT COUNT(1) AS count
FROM moz_places h
WHERE h.url = :page_url
WHERE h.url_hash = hash(:page_url) AND h.url = :page_url
AND h.title = :title`
);
stmt.params.page_url = uri.spec;
@ -841,11 +839,13 @@ add_task(function* test_referrer_saved() {
let stmt = DBConn().createStatement(
`SELECT COUNT(1) AS count
FROM moz_historyvisits
WHERE place_id = (SELECT id FROM moz_places WHERE url = :page_url)
JOIN moz_places h ON h.id = place_id
WHERE url_hash = hash(:page_url) AND url = :page_url
AND from_visit = (
SELECT id
FROM moz_historyvisits
WHERE place_id = (SELECT id FROM moz_places WHERE url = :referrer)
SELECT v.id
FROM moz_historyvisits v
JOIN moz_places h ON h.id = place_id
WHERE url_hash = hash(:referrer) AND url = :referrer
)`
);
stmt.params.page_url = uri.spec;
@ -1117,8 +1117,9 @@ add_task(function* test_typed_hidden_not_overwritten() {
yield promiseUpdatePlaces(places);
let db = yield PlacesUtils.promiseDBConnection();
let rows = yield db.execute("SELECT hidden, typed FROM moz_places WHERE url = :url",
{ url: "http://mozilla.org/" });
let rows = yield db.execute(
"SELECT hidden, typed FROM moz_places WHERE url_hash = hash(:url) AND 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,

Просмотреть файл

@ -131,7 +131,8 @@ add_task(function* test_history_clear()
// Check that all moz_places entries except bookmarks and place: have been removed
stmt = mDBConn.createStatement(
`SELECT h.id FROM moz_places h WHERE SUBSTR(h.url, 1, 6) <> 'place:'
`SELECT h.id FROM moz_places h WHERE
url_hash NOT BETWEEN hash('place', 'prefix_lo') AND hash('place', 'prefix_hi')
AND NOT EXISTS (SELECT id FROM moz_bookmarks WHERE fk = h.id) LIMIT 1`);
do_check_false(stmt.executeStep());
stmt.finalize();
@ -160,7 +161,9 @@ add_task(function* test_history_clear()
// Check that place:uris have frecency 0
stmt = mDBConn.createStatement(
`SELECT h.id FROM moz_places h
WHERE SUBSTR(h.url, 1, 6) = 'place:' AND h.frecency <> 0 LIMIT 1`);
WHERE url_hash BETWEEN hash('place', 'prefix_lo')
AND hash('place', 'prefix_hi')
AND h.frecency <> 0 LIMIT 1`);
do_check_false(stmt.executeStep());
stmt.finalize();
});

Просмотреть файл

@ -18,7 +18,7 @@ function isHostInMozPlaces(aURI)
let stmt = DBConn().createStatement(
`SELECT url
FROM moz_places
WHERE url = :host`
WHERE url_hash = hash(:host) AND url = :host`
);
let result = false;
stmt.params.host = aURI.spec;

Просмотреть файл

@ -42,7 +42,7 @@ function cleanDatabase() {
function addPlace(aUrl, aFavicon) {
let stmt = mDBConn.createStatement(
"INSERT INTO moz_places (url, favicon_id) VALUES (:url, :favicon)");
"INSERT INTO moz_places (url, url_hash, favicon_id) VALUES (:url, hash(:url), :favicon)");
stmt.params["url"] = aUrl || "http://www.mozilla.org";
stmt.params["favicon"] = aFavicon || null;
stmt.execute();
@ -1077,7 +1077,8 @@ tests.push({
setup: function* () {
function setVisitCount(aURL, aValue) {
let stmt = mDBConn.createStatement(
"UPDATE moz_places SET visit_count = :count WHERE url = :url"
`UPDATE moz_places SET visit_count = :count WHERE url_hash = hash(:url)
AND url = :url`
);
stmt.params.count = aValue;
stmt.params.url = aURL;
@ -1086,7 +1087,8 @@ tests.push({
}
function setLastVisitDate(aURL, aValue) {
let stmt = mDBConn.createStatement(
"UPDATE moz_places SET last_visit_date = :date WHERE url = :url"
`UPDATE moz_places SET last_visit_date = :date WHERE url_hash = hash(:url)
AND url = :url`
);
stmt.params.date = aValue;
stmt.params.url = aURL;
@ -1151,8 +1153,8 @@ tests.push({
name: "L.3",
desc: "recalculate hidden for redirects.",
setup: function() {
PlacesTestUtils.addVisits([
*setup() {
yield PlacesTestUtils.addVisits([
{ uri: NetUtil.newURI("http://l3.moz.org/"),
transition: TRANSITION_TYPED },
{ uri: NetUtil.newURI("http://l3.moz.org/redirecting/"),
@ -1197,6 +1199,61 @@ tests.push({
//------------------------------------------------------------------------------
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",
@ -1261,19 +1318,8 @@ tests.push({
//------------------------------------------------------------------------------
// 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();

Просмотреть файл

@ -931,9 +931,10 @@ function openConnection(options) {
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);
}
});
@ -1012,6 +1013,7 @@ function cloneStorageConnection(options) {
resolve(new OpenedConnection(conn, identifier, openedOptions));
} catch (ex) {
log.warn("Could not clone database", ex);
connection.asyncClose();
reject(ex);
}
});