Bug 1512868 - Simplify merge triggers in the Sync mirror. r=mak

This commit consolidates the `insertNewLocalItems` and
`updateExistingLocalItems` triggers into a single trigger that uses
an upsert, and removes the last vestiges of annotations from the
mirror.

Differential Revision: https://phabricator.services.mozilla.com/D14035

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Lina Cambridge 2018-12-12 00:56:29 +00:00
Родитель a51c634adf
Коммит 38d15752db
1 изменённых файлов: 46 добавлений и 235 удалений

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

@ -650,7 +650,6 @@ class SyncedBookmarksMirror {
await this.db.execute(`DELETE FROM itemsChanged`);
await this.db.execute(`DELETE FROM itemsRemoved`);
await this.db.execute(`DELETE FROM itemsMoved`);
await this.db.execute(`DELETE FROM annosChanged`);
}
},
time => this.recordTelemetryEvent("mirror", "apply",
@ -1462,23 +1461,12 @@ class SyncedBookmarksMirror {
${LocalItemsSQLFragment}
INSERT INTO itemsToUpload(id, guid, syncChangeCounter, parentGuid,
parentTitle, dateAdded, type, title, placeId,
isQuery, url, keyword, feedURL, siteURL,
position, tagFolderName)
isQuery, url, keyword, position, tagFolderName)
SELECT s.id, s.guid, s.syncChangeCounter, s.parentGuid, s.parentTitle,
s.dateAdded / 1000, s.type, s.title, s.placeId,
IFNULL(SUBSTR(h.url, 1, 6) = 'place:', 0) AS isQuery,
h.url,
(SELECT keyword FROM moz_keywords WHERE place_id = h.id),
(SELECT a.content FROM moz_items_annos a
JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id
WHERE s.type = :folderType AND
a.item_id = s.id AND
n.name = :feedURLAnno),
(SELECT a.content FROM moz_items_annos a
JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id
WHERE s.type = :folderType AND
a.item_id = s.id AND
n.name = :siteURLAnno),
s.position,
(SELECT get_query_param(substr(url, 7), 'tag')
WHERE substr(h.url, 1, 6) = 'place:')
@ -1486,10 +1474,7 @@ class SyncedBookmarksMirror {
LEFT JOIN moz_places h ON h.id = s.placeId
LEFT JOIN idsToWeaklyUpload w ON w.id = s.id
WHERE s.syncChangeCounter >= 1 OR
w.id NOT NULL`,
{ folderType: PlacesUtils.bookmarks.TYPE_FOLDER,
feedURLAnno: PlacesUtils.LMANNO_FEEDURI,
siteURLAnno: PlacesUtils.LMANNO_SITEURI });
w.id NOT NULL`);
// Record the child GUIDs of locally changed folders, which we use to
// populate the `children` array in the record.
@ -1557,7 +1542,7 @@ class SyncedBookmarksMirror {
let itemRows = await this.db.execute(`
SELECT id, syncChangeCounter, guid, isDeleted, type, isQuery,
tagFolderName, keyword, url, IFNULL(title, "") AS title,
feedURL, siteURL, position, parentGuid,
position, parentGuid,
IFNULL(parentTitle, "") AS parentTitle, dateAdded
FROM itemsToUpload`);
@ -1937,19 +1922,6 @@ async function createMirrorRoots(db) {
* The mirror database connection.
*/
async function initializeTempMirrorEntities(db) {
// Columns in `items` that correspond to annos stored in `moz_items_annos`.
// We use this table to build SQL fragments for the `insertNewLocalItems` and
// `updateExistingLocalItems` triggers below.
const syncedAnnoTriggers = [{
annoName: PlacesUtils.LMANNO_FEEDURI,
columnName: "newFeedURL",
type: PlacesUtils.annotations.TYPE_STRING,
}, {
annoName: PlacesUtils.LMANNO_SITEURI,
columnName: "newSiteURL",
type: PlacesUtils.annotations.TYPE_STRING,
}];
// Stores the value and structure states of all nodes in the merged tree.
await db.execute(`CREATE TEMP TABLE mergeStates(
localGuid TEXT NOT NULL,
@ -2005,7 +1977,8 @@ async function initializeTempMirrorEntities(db) {
/* Trigger frecency updates for all affected origins. */
DELETE FROM moz_updateoriginsupdate_temp;
/* Remove annos for the deleted items. */
/* Remove annos for the deleted items. This can be removed in bug
1460577. */
DELETE FROM moz_items_annos
WHERE item_id = (SELECT id FROM moz_bookmarks
WHERE guid = OLD.guid);
@ -2046,8 +2019,7 @@ async function initializeTempMirrorEntities(db) {
CREATE TEMP VIEW itemsToMerge(localId, remoteId, useRemote, shouldUpload,
newLevel, oldGuid, newGuid, newType,
newDateAddedMicroseconds, newTitle,
oldPlaceId, newPlaceId, newKeyword,
newFeedURL, newSiteURL) AS
oldPlaceId, newPlaceId, newKeyword) AS
SELECT b.id, v.id, r.useRemote, r.shouldUpload,
r.level, r.localGuid, r.mergedGuid,
(CASE WHEN v.kind IN (${[
@ -2066,7 +2038,7 @@ async function initializeTempMirrorEntities(db) {
v.title, h.id, (SELECT n.id FROM moz_places n
WHERE n.url_hash = u.hash AND
n.url = u.url),
v.keyword, v.feedURL, v.siteURL
v.keyword
FROM mergeStates r
LEFT JOIN items v ON v.guid = r.mergedGuid
LEFT JOIN moz_bookmarks b ON b.guid = r.localGuid
@ -2110,98 +2082,19 @@ async function initializeTempMirrorEntities(db) {
id = OLD.remoteId;
END`);
// Inserts items from the mirror that don't exist locally.
await db.execute(`
CREATE TEMP TRIGGER insertNewLocalItems
INSTEAD OF DELETE ON itemsToMerge WHEN OLD.useRemote AND
OLD.localId IS NULL
CREATE TEMP TRIGGER updateLocalItems
INSTEAD OF DELETE ON itemsToMerge WHEN OLD.useRemote
BEGIN
/* Record an item added notification for the new item. */
INSERT INTO itemsAdded(guid, keywordChanged, level)
VALUES(OLD.newGuid, OLD.newKeyword NOT NULL OR
SELECT OLD.newGuid, OLD.newKeyword NOT NULL OR
EXISTS(SELECT 1 FROM moz_keywords
WHERE place_id = OLD.newPlaceId OR
keyword = OLD.newKeyword),
OLD.newLevel);
OLD.newLevel
WHERE OLD.localId IS NULL;
/* Sync associates keywords with bookmarks, and doesn't sync POST data;
Places associates keywords with (URL, POST data) pairs, and multiple
bookmarks may have the same URL. For simplicity, we bump the change
counter for all local bookmarks with the remote URL (bug 1328737),
then remove all local keywords from remote URLs, and the remote keyword
from local URLs. We intentionally use "place_id = OLD.newPlaceId"
instead of "fk = OLD.newPlaceId OR fk IN (...)" in the WHERE clause
because we only want to bump the counter if the URL has keywords. */
INSERT OR IGNORE INTO relatedIdsToReupload(id)
SELECT b.id FROM moz_bookmarks b
JOIN moz_keywords k ON k.place_id = b.fk
WHERE k.place_id = OLD.newPlaceId OR
k.keyword = OLD.newKeyword;
/* Remove the new keyword from existing items, and all keywords from the
new URL. */
DELETE FROM moz_keywords WHERE place_id = OLD.newPlaceId OR
keyword = OLD.newKeyword;
/* Remove existing tags for the new URL. */
DELETE FROM localTags WHERE placeId = OLD.newPlaceId;
/* Insert the new item, using "-1" as the placeholder parent and
position. We'll update these later, in the "updateLocalStructure"
trigger. */
INSERT INTO moz_bookmarks(guid, parent, position, type, fk, title,
dateAdded, lastModified, syncStatus,
syncChangeCounter)
VALUES(OLD.newGuid, -1, -1, OLD.newType, OLD.newPlaceId, OLD.newTitle,
OLD.newDateAddedMicroseconds,
STRFTIME('%s', 'now', 'localtime', 'utc') * 1000000,
${PlacesUtils.bookmarks.SYNC_STATUS.NORMAL}, OLD.shouldUpload);
/* Insert a new keyword for the new URL, if one is set. */
INSERT OR IGNORE INTO moz_keywords(keyword, place_id, post_data)
SELECT OLD.newKeyword, OLD.newPlaceId, ''
WHERE OLD.newKeyword NOT NULL;
/* Insert new tags for the new URL. */
INSERT INTO localTags(tag, placeId)
SELECT t.tag, OLD.newPlaceId FROM tags t
WHERE t.itemId = OLD.remoteId;
/* Insert new synced annos. These are almost identical to the statements
for updates, except we need an additional subquery to fetch the new
item's ID. We can also skip removing existing annos. */
INSERT OR IGNORE INTO moz_anno_attributes(name)
VALUES ${syncedAnnoTriggers.map(annoTrigger =>
`('${annoTrigger.annoName}')`
).join(",")};
${syncedAnnoTriggers.map(annoTrigger => `
INSERT INTO moz_items_annos(item_id, anno_attribute_id, content, flags,
expiration, type, lastModified, dateAdded)
SELECT (SELECT id FROM moz_bookmarks
WHERE guid = OLD.newGuid),
(SELECT id FROM moz_anno_attributes
WHERE name = '${annoTrigger.annoName}'),
OLD.${annoTrigger.columnName}, 0,
${PlacesUtils.annotations.EXPIRE_NEVER}, ${annoTrigger.type},
STRFTIME('%s', 'now', 'localtime', 'utc') * 1000000,
STRFTIME('%s', 'now', 'localtime', 'utc') * 1000000
WHERE OLD.${annoTrigger.columnName} NOT NULL;
/* Record an anno set notification for the new synced anno. */
REPLACE INTO annosChanged(itemId, annoName, wasRemoved)
SELECT b.id, '${annoTrigger.annoName}', 0 FROM moz_bookmarks b
WHERE b.guid = OLD.newGuid AND
OLD.${annoTrigger.columnName} NOT NULL;
`).join("")}
END`);
// Updates existing items with new values from the mirror.
await db.execute(`
CREATE TEMP TRIGGER updateExistingLocalItems
INSTEAD OF DELETE ON itemsToMerge WHEN OLD.useRemote AND
OLD.localId NOT NULL
BEGIN
/* Record an item changed notification for the existing item. */
INSERT INTO itemsChanged(itemId, oldTitle, oldPlaceId, keywordChanged,
level)
@ -2211,26 +2104,26 @@ async function initializeTempMirrorEntities(db) {
keyword = OLD.newKeyword),
OLD.newLevel
FROM moz_bookmarks
WHERE id = OLD.localId;
WHERE OLD.localId NOT NULL AND
id = OLD.localId;
UPDATE moz_bookmarks SET
title = OLD.newTitle,
dateAdded = OLD.newDateAddedMicroseconds,
lastModified = STRFTIME('%s', 'now', 'localtime', 'utc') * 1000000
WHERE id = OLD.localId;
/* Bump the change counter for items with the old URL, new URL, and new
keyword. */
/* Sync associates keywords with bookmarks, and doesn't sync POST data;
Places associates keywords with (URL, POST data) pairs, and multiple
bookmarks may have the same URL. For consistency (bug 1328737), we
reupload all items with the old URL, new URL, and new keyword. Note
that we intentionally use "k.place_id IN (...)" instead of
"b.fk = OLD.newPlaceId OR fk IN (...)" in the WHERE clause because we
only want to reupload items with keywords. */
INSERT OR IGNORE INTO relatedIdsToReupload(id)
SELECT b.id FROM moz_bookmarks b
JOIN moz_keywords k ON k.place_id = b.fk
WHERE b.id <> OLD.localId AND (
WHERE (b.id <> OLD.localId OR OLD.localId IS NULL) AND (
k.place_id IN (OLD.oldPlaceId, OLD.newPlaceId) OR
k.keyword = OLD.newKeyword
);
/* Remove the new keyword from existing items, and all keywords from the
old and new URLs. */
/* Remove all keywords from the old and new URLs, and remove the new
keyword from all existing URLs. */
DELETE FROM moz_keywords WHERE place_id IN (OLD.oldPlaceId,
OLD.newPlaceId) OR
keyword = OLD.newKeyword;
@ -2238,24 +2131,30 @@ async function initializeTempMirrorEntities(db) {
/* Remove existing tags. */
DELETE FROM localTags WHERE placeId IN (OLD.oldPlaceId, OLD.newPlaceId);
/* Update the URL and recalculate frecency. It's important we do this
*after* removing old keywords and *before* inserting new ones, so that
the above statements select the correct affected items. */
UPDATE moz_bookmarks SET
fk = OLD.newPlaceId
WHERE OLD.oldPlaceId <> OLD.newPlaceId AND
id = OLD.localId;
/* Insert the new item, using "-1" as the placeholder parent and
position. We'll update these later, in the "updateLocalStructure"
trigger. */
INSERT INTO moz_bookmarks(id, guid, parent, position, type, fk, title,
dateAdded, lastModified, syncStatus,
syncChangeCounter)
VALUES(OLD.localId, OLD.newGuid, -1, -1, OLD.newType, OLD.newPlaceId,
OLD.newTitle, OLD.newDateAddedMicroseconds,
STRFTIME('%s', 'now', 'localtime', 'utc') * 1000000,
${PlacesUtils.bookmarks.SYNC_STATUS.NORMAL}, OLD.shouldUpload)
ON CONFLICT(id) DO UPDATE SET
title = excluded.title,
dateAdded = excluded.dateAdded,
lastModified = excluded.lastModified,
/* It's important that we update the URL *after* removing old keywords
and *before* inserting new ones, so that the above DELETEs select
the correct affected items. */
fk = excluded.fk;
/* Recalculate frecency. */
UPDATE moz_places SET
frecency = -frecency
WHERE OLD.oldPlaceId <> OLD.newPlaceId AND
id = OLD.oldPlaceId AND
frecency > 0;
UPDATE moz_places SET
frecency = -frecency
WHERE OLD.oldPlaceId <> OLD.newPlaceId AND
id = OLD.newPlaceId AND
id IN (OLD.oldPlaceId, OLD.newPlaceId) AND
frecency > 0;
/* Trigger frecency updates for all affected origins. */
@ -2270,47 +2169,6 @@ async function initializeTempMirrorEntities(db) {
INSERT INTO localTags(tag, placeId)
SELECT t.tag, OLD.newPlaceId FROM tags t
WHERE t.itemId = OLD.remoteId;
/* Record anno removed notifications for the synced annos. */
REPLACE INTO annosChanged(itemId, annoName, wasRemoved)
SELECT a.item_id, n.name, 1 FROM moz_items_annos a
JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id
WHERE item_id = OLD.localId AND
anno_attribute_id IN (SELECT id FROM moz_anno_attributes
WHERE name IN (${syncedAnnoTriggers.map(
annoTrigger => `'${annoTrigger.annoName}'`
).join(",")}));
/* Remove existing synced annos. */
DELETE FROM moz_items_annos
WHERE item_id = OLD.localId AND
anno_attribute_id IN (SELECT id FROM moz_anno_attributes
WHERE name IN (${syncedAnnoTriggers.map(
annoTrigger => `'${annoTrigger.annoName}'`
).join(",")}));
/* Insert new synced annos. */
INSERT OR IGNORE INTO moz_anno_attributes(name)
VALUES ${syncedAnnoTriggers.map(annoTrigger =>
`('${annoTrigger.annoName}')`
).join(",")};
${syncedAnnoTriggers.map(annoTrigger => `
INSERT INTO moz_items_annos(item_id, anno_attribute_id, content, flags,
expiration, type, lastModified, dateAdded)
SELECT OLD.localId, (SELECT id FROM moz_anno_attributes
WHERE name = '${annoTrigger.annoName}'),
OLD.${annoTrigger.columnName}, 0,
${PlacesUtils.annotations.EXPIRE_NEVER}, ${annoTrigger.type},
STRFTIME('%s', 'now', 'localtime', 'utc') * 1000000,
STRFTIME('%s', 'now', 'localtime', 'utc') * 1000000
WHERE OLD.${annoTrigger.columnName} NOT NULL;
/* Record an anno set notification for the new synced anno. */
REPLACE INTO annosChanged(itemId, annoName, wasRemoved)
SELECT OLD.localId, '${annoTrigger.annoName}', 0
WHERE OLD.${annoTrigger.columnName} NOT NULL;
`).join("")}
END`);
// A view of the structure states for all items in the merged tree. The
@ -2510,14 +2368,6 @@ async function initializeTempMirrorEntities(db) {
isUntagging BOOLEAN NOT NULL DEFAULT 0
) WITHOUT ROWID`);
// Stores properties to pass to `onItemChanged` bookmark observers.
await db.execute(`CREATE TEMP TABLE annosChanged(
itemId INTEGER NOT NULL,
annoName TEXT NOT NULL,
wasRemoved BOOLEAN NOT NULL,
PRIMARY KEY(itemId, annoName, wasRemoved)
) WITHOUT ROWID`);
// Stores local IDs for items to upload even if they're not flagged as changed
// in Places. These are "weak" because we won't try to reupload the item on
// the next sync if the upload is interrupted or fails.
@ -2559,8 +2409,6 @@ async function initializeTempMirrorEntities(db) {
url TEXT,
tagFolderName TEXT,
keyword TEXT,
feedURL TEXT,
siteURL TEXT,
position INTEGER
)`);
@ -4530,8 +4378,8 @@ BookmarkMerger.STRUCTURE = {
};
/**
* Fires bookmark, annotation, and keyword observer notifications for all
* changes made during the merge.
* Fires bookmark and keyword observer notifications for all changes made during
* the merge.
*/
class BookmarkObserverRecorder {
constructor(db, { maxFrecenciesToRecalculate }) {
@ -4704,28 +4552,6 @@ class BookmarkObserverRecorder {
this.noteItemChanged(info);
}
MirrorLog.trace("Recording observer notifications for changed annos");
let annoRows = await this.db.execute(`
SELECT b.id, b.guid, b.lastModified, b.type, p.id AS parentId,
p.guid AS parentGuid, c.annoName, c.wasRemoved
FROM annosChanged c
JOIN moz_bookmarks b ON b.id = c.itemId
JOIN moz_bookmarks p ON p.id = b.parent
LEFT JOIN moz_places h ON h.id = b.fk
ORDER BY p.id, b.position, c.wasRemoved <> 1`);
for await (let row of yieldingIterator(annoRows)) {
this.noteAnnoChanged({
id: row.getResultByName("id"),
name: row.getResultByName("annoName"),
wasRemoved: !!row.getResultByName("wasRemoved"),
guid: row.getResultByName("guid"),
lastModified: row.getResultByName("lastModified"),
type: row.getResultByName("type"),
parentId: row.getResultByName("parentId"),
parentGuid: row.getResultByName("parentGuid"),
});
}
MirrorLog.trace("Recording notifications for changed keywords");
let keywordsChangedRows = await this.db.execute(`
SELECT EXISTS(SELECT 1 FROM itemsAdded WHERE keywordChanged) OR
@ -4805,21 +4631,6 @@ class BookmarkObserverRecorder {
});
}
noteAnnoChanged(info) {
if (info.name != PlacesUtils.LMANNO_FEEDURI &&
info.name != PlacesUtils.LMANNO_SITEURI) {
throw new TypeError("Can't record change for unsupported anno");
}
this.bookmarkObserverNotifications.push({
name: "onItemChanged",
isTagging: false,
args: [info.id, info.name, /* isAnnotationProperty */ true,
/* newValue */ "", info.lastModified, info.type, info.parentId,
info.guid, info.parentGuid, /* oldValue */ "",
PlacesUtils.bookmarks.SOURCES.SYNC],
});
}
async notifyBookmarkObservers() {
MirrorLog.trace("Notifying bookmark observers");
let observers = PlacesUtils.bookmarks.getObservers();