Bug 1258127 - Update the Places schema to track bookmark sync changes. r=mak,rnewman
authorKit Cambridge <kcambridge@mozilla.com>
Sun, 20 Nov 2016 08:22:02 -0800
changeset 323402 e2ef58201bcc5f5a5d9260fdf330a8171f250ebe
parent 323401 3ce8e7ccf045dafe326157cbd29af2eeebf5518e
child 323403 e614e5545a0c191a89c8503cd3eeb0db2c301e71
push id21
push usermaklebus@msu.edu
push dateThu, 01 Dec 2016 06:22:08 +0000
reviewersmak, rnewman
bugs1258127
milestone53.0a1
Bug 1258127 - Update the Places schema to track bookmark sync changes. r=mak,rnewman MozReview-Commit-ID: K27JzgU4KnB
toolkit/components/places/Database.cpp
toolkit/components/places/Database.h
toolkit/components/places/nsINavBookmarksService.idl
toolkit/components/places/nsPlacesTables.h
toolkit/components/places/tests/head_common.js
toolkit/components/places/tests/migration/places_v36.sqlite
toolkit/components/places/tests/migration/test_current_from_v35.js
toolkit/components/places/tests/migration/xpcshell.ini
--- a/toolkit/components/places/Database.cpp
+++ b/toolkit/components/places/Database.cpp
@@ -269,20 +269,22 @@ CreateRoot(nsCOMPtr<mozIStorageConnectio
   static PRTime timestamp = 0;
   if (!timestamp)
     timestamp = RoundedPRNow();
 
   // Create a new bookmark folder for the root.
   nsCOMPtr<mozIStorageStatement> stmt;
   nsresult rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
     "INSERT INTO moz_bookmarks "
-      "(type, position, title, dateAdded, lastModified, guid, parent) "
+      "(type, position, title, dateAdded, lastModified, guid, parent, "
+       "syncChangeCounter, syncStatus) "
     "VALUES (:item_type, :item_position, :item_title,"
-            ":date_added, :last_modified, :guid,"
-            "IFNULL((SELECT id FROM moz_bookmarks WHERE parent = 0), 0))"
+            ":date_added, :last_modified, :guid, "
+            "IFNULL((SELECT id FROM moz_bookmarks WHERE parent = 0), 0), "
+            "1, :sync_status)"
   ), getter_AddRefs(stmt));
   if (NS_FAILED(rv)) return rv;
 
   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_type"),
                              nsINavBookmarksService::TYPE_FOLDER);
   if (NS_FAILED(rv)) return rv;
   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_position"), itemPosition);
   if (NS_FAILED(rv)) return rv;
@@ -290,16 +292,19 @@ CreateRoot(nsCOMPtr<mozIStorageConnectio
                                   NS_ConvertUTF16toUTF8(titleString));
   if (NS_FAILED(rv)) return rv;
   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("date_added"), timestamp);
   if (NS_FAILED(rv)) return rv;
   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("last_modified"), timestamp);
   if (NS_FAILED(rv)) return rv;
   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), aGuid);
   if (NS_FAILED(rv)) return rv;
+  rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("sync_status"),
+                             nsINavBookmarksService::SYNC_STATUS_NEW);
+  if (NS_FAILED(rv)) return rv;
   rv = stmt->Execute();
   if (NS_FAILED(rv)) return rv;
 
   // The 'places' root is a folder containing the other roots.
   // The first bookmark in a folder has position 0.
   if (!aRootName.EqualsLiteral("places"))
     ++itemPosition;
 
@@ -872,16 +877,23 @@ Database::InitSchema(bool* aDatabaseMigr
 
       if (currentSchemaVersion < 35) {
         rv = MigrateV35Up();
         NS_ENSURE_SUCCESS(rv, rv);
       }
 
       // Firefox 52 uses schema version 35.
 
+      if (currentSchemaVersion < 36) {
+        rv = MigrateV36Up();
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+
+      // Firefox 53 uses schema version 36.
+
       // 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));
     }
   }
@@ -922,16 +934,18 @@ Database::InitSchema(bool* aDatabaseMigr
 
     // moz_hosts.
     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_HOSTS);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // moz_bookmarks.
     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_BOOKMARKS);
     NS_ENSURE_SUCCESS(rv, rv);
+    rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_BOOKMARKS_DELETED);
+    NS_ENSURE_SUCCESS(rv, rv);
     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_PLACETYPE);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_PARENTPOSITION);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_PLACELASTMODIFIED);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_GUID);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -1028,16 +1042,37 @@ Database::CreateBookmarkRoots()
                                  getter_Copies(rootTitle));
   if (NS_FAILED(rv)) return rv;
   rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("unfiled"),
                   NS_LITERAL_CSTRING("unfiled_____"), rootTitle);
   if (NS_FAILED(rv)) return rv;
 
   int64_t mobileRootId = CreateMobileRoot();
   if (mobileRootId <= 0) return NS_ERROR_FAILURE;
+  {
+    nsCOMPtr<mozIStorageStatement> mobileRootSyncStatusStmt;
+    rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
+      "UPDATE moz_bookmarks SET syncStatus = :sync_status WHERE id = :id"
+    ), getter_AddRefs(mobileRootSyncStatusStmt));
+    if (NS_FAILED(rv)) return rv;
+    mozStorageStatementScoper mobileRootSyncStatusScoper(
+      mobileRootSyncStatusStmt);
+
+    rv = mobileRootSyncStatusStmt->BindInt32ByName(
+      NS_LITERAL_CSTRING("sync_status"),
+      nsINavBookmarksService::SYNC_STATUS_NEW
+    );
+    if (NS_FAILED(rv)) return rv;
+    rv = mobileRootSyncStatusStmt->BindInt64ByName(NS_LITERAL_CSTRING("id"),
+                                                   mobileRootId);
+    if (NS_FAILED(rv)) return rv;
+
+    rv = mobileRootSyncStatusStmt->Execute();
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
 
 #if DEBUG
   nsCOMPtr<mozIStorageStatement> stmt;
   rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT count(*), sum(position) FROM moz_bookmarks"
   ), getter_AddRefs(stmt));
   if (NS_FAILED(rv)) return rv;
 
@@ -1907,16 +1942,62 @@ Database::MigrateV35Up() {
     rv = DeleteBookmarkItem(folderIds[i]);
     if (NS_FAILED(rv)) return rv;
   }
 
   return NS_OK;
 }
 
 nsresult
+Database::MigrateV36Up() {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // Add sync status and change counter tracking columns for bookmarks.
+  nsCOMPtr<mozIStorageStatement> syncStatusStmt;
+  nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
+    "SELECT syncStatus FROM moz_bookmarks"
+  ), getter_AddRefs(syncStatusStmt));
+  if (NS_FAILED(rv)) {
+    // We default to SYNC_STATUS_UNKNOWN = 0 for existing bookmarks, matching
+    // the bookmark restore behavior. If Sync is set up, we'll update the status
+    // to SYNC_STATUS_NORMAL = 2 before the first post-migration sync.
+    rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+      "ALTER TABLE moz_bookmarks "
+      "ADD COLUMN syncStatus INTEGER DEFAULT 0 NOT NULL"
+    ));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  nsCOMPtr<mozIStorageStatement> syncChangeCounterStmt;
+  rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
+    "SELECT syncChangeCounter FROM moz_bookmarks"
+  ), getter_AddRefs(syncChangeCounterStmt));
+  if (NS_FAILED(rv)) {
+    // The change counter starts at 1 for all local bookmarks. It's incremented
+    // for each modification that should trigger a sync, and decremented after
+    // the modified bookmark is uploaded to the server.
+    rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+      "ALTER TABLE moz_bookmarks "
+      "ADD COLUMN syncChangeCounter INTEGER DEFAULT 1 NOT NULL"));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  nsCOMPtr<mozIStorageStatement> tombstoneTableStmt;
+  rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
+    "SELECT 1 FROM moz_bookmarks_deleted"
+  ), getter_AddRefs(tombstoneTableStmt));
+  if (NS_FAILED(rv)) {
+    rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_BOOKMARKS_DELETED);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return NS_OK;
+}
+
+nsresult
 Database::GetItemsWithAnno(const nsACString& aAnnoName, int32_t aItemType,
                            nsTArray<int64_t>& aItemIds)
 {
   nsCOMPtr<mozIStorageStatement> stmt;
   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT b.id FROM moz_items_annos a "
     "JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id "
     "JOIN moz_bookmarks b ON b.id = a.item_id "
--- 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 35
+#define DATABASE_SCHEMA_VERSION 36
 
 // 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.
@@ -266,16 +266,17 @@ protected:
   nsresult MigrateV27Up();
   nsresult MigrateV28Up();
   nsresult MigrateV30Up();
   nsresult MigrateV31Up();
   nsresult MigrateV32Up();
   nsresult MigrateV33Up();
   nsresult MigrateV34Up();
   nsresult MigrateV35Up();
+  nsresult MigrateV36Up();
 
   nsresult UpdateBookmarkRootTitles();
 
   friend class ConnectionShutdownBlocker;
 
   int64_t CreateMobileRoot();
   nsresult GetItemsWithAnno(const nsACString& aAnnoName, int32_t aItemType,
                             nsTArray<int64_t>& aItemIds);
--- a/toolkit/components/places/nsINavBookmarksService.idl
+++ b/toolkit/components/places/nsINavBookmarksService.idl
@@ -315,16 +315,32 @@ interface nsINavBookmarksService : nsISu
   // be extended to support other callers. Sources are passed as optional
   // parameters to methods used by Sync, and forwarded to observers.
   const unsigned short SOURCE_DEFAULT = 0;
   const unsigned short SOURCE_SYNC = 1;
   const unsigned short SOURCE_IMPORT = 2;
   const unsigned short SOURCE_IMPORT_REPLACE = 3;
 
   /**
+   * Sync status flags.
+   */
+  // "UNKNOWN" means sync tracking information was lost because the item
+  // was restored from a backup, and needs to be reconciled with the server.
+  // This is set for migrated and restored bookmarks, and changed to "NORMAL"
+  // before upload.
+  const unsigned short SYNC_STATUS_UNKNOWN = 0;
+  // "NEW" means the item has never been synced, so we don't need to write
+  // a tombstone if it's deleted. This is set for all new local bookmarks, and
+  // changed to "NORMAL" before upload.
+  const unsigned short SYNC_STATUS_NEW = 1;
+  // "NORMAL" means the item has been uploaded to the server, and needs a
+  // tombstone if it's deleted.
+  const unsigned short SYNC_STATUS_NORMAL = 2;
+
+  /**
    * Inserts a child bookmark into the given folder.
    *
    *  @param aParentId
    *         The id of the parent folder
    *  @param aURI
    *         The URI to insert
    *  @param aIndex
    *         The index to insert at, or DEFAULT_INDEX to append
--- a/toolkit/components/places/nsPlacesTables.h
+++ b/toolkit/components/places/nsPlacesTables.h
@@ -102,16 +102,44 @@
     ", parent INTEGER" \
     ", position INTEGER" \
     ", title LONGVARCHAR" \
     ", keyword_id INTEGER" \
     ", folder_type TEXT" \
     ", dateAdded INTEGER" \
     ", lastModified INTEGER" \
     ", guid TEXT" \
+    /* The sync status is determined from the change source. We set this to
+       SYNC_STATUS_NEW = 1 for new local bookmarks, and SYNC_STATUS_NORMAL = 2
+       for bookmarks from other devices. Uploading a local bookmark for the
+       first time changes its status to SYNC_STATUS_NORMAL. For bookmarks
+       restored from a backup, we set SYNC_STATUS_UNKNOWN = 0, indicating that
+       Sync should reconcile them with bookmarks on the server. If Sync is
+       disconnected or never set up, all bookmarks will stay in SYNC_STATUS_NEW.
+    */ \
+    ", syncStatus INTEGER NOT NULL DEFAULT 0" \
+    /* This field is incremented for every bookmark change that should trigger
+       a sync. It's a counter instead of a Boolean so that we can track changes
+       made during a sync, and queue them for the next sync. Changes made by
+       Sync don't bump the counter, to avoid sync loops. If Sync is
+       disconnected, we'll reset the counter to 1 for all bookmarks.
+    */ \
+    ", syncChangeCounter INTEGER NOT NULL DEFAULT 1" \
+  ")" \
+)
+
+// This table stores tombstones for bookmarks with SYNC_STATUS_NORMAL. We
+// upload tombstones during a sync, and delete them from this table on success.
+// If Sync is disconnected, we'll delete all stored tombstones. If Sync is
+// never set up, we'll never write new tombstones, since all bookmarks will stay
+// in SYNC_STATUS_NEW.
+#define CREATE_MOZ_BOOKMARKS_DELETED NS_LITERAL_CSTRING( \
+  "CREATE TABLE moz_bookmarks_deleted (" \
+    "  guid TEXT PRIMARY KEY" \
+    ", dateRemoved INTEGER NOT NULL DEFAULT 0" \
   ")" \
 )
 
 #define CREATE_MOZ_KEYWORDS NS_LITERAL_CSTRING( \
   "CREATE TABLE moz_keywords (" \
     "  id INTEGER PRIMARY KEY AUTOINCREMENT" \
     ", keyword TEXT UNIQUE" \
     ", place_id INTEGER" \
--- 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 = 35;
+const CURRENT_SCHEMA_VERSION = 36;
 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;
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a2b180111fb3e3a06a872a125308525ba7972f70
GIT binary patch
literal 1212416
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
zE@SV1o{s&^Uk|(8*Zt4_JEji!?XkOW^X19h9^cKw!GLwK;|ADWiS0iG2Ha70+DO|f
z){GlabEhAE+Y0Y+TWL+D6Uk+rdpe1MOStbpd$wJV%KQ>L@5WGYb#VOpN7kRa{)lx?
zuKQRn0RR910000000000000000000000000000000002^N3}6sHZ*kp!I_%!va)cv
zWMo<K@@%#%b8KyGPfyR-j?~&@dwYCrD!r^W(OI)(QEh87ooG$1uI)*7wxoJ8HRI}P
zdlK!<sg6YL3EkO_#!RX^-JF=#SFENfohy_`AC=GQNVFuoJ9eAZoa)RbI<xt_Og7!!
zc~Wng*yz|9z5T?-)s2lUNTgd6&DmHswJecco=A_4{bIxAvAMltT$WCajkmWa*QPqg
z=7(O|6e_MCGvVIDwzkZvUwBf(+EuN~>N`gtaL}`dUOTIFNa(cC(6X|!P+4eXc}Z7$
zyg6}f{;$nXn{-qrmCjBZH{qy``09m;Om}-WGcDKo855UeX3lJ#KX1W`no#Ja2}@3S
zaLlxlA))EJG*hTQWvA-%8(Nz)r_Nt6e$LwQ`RbE?dh<ulC>|185~|5n4;SUCzrn=E
zn^N7`+MUm`IJBvK#?qPN>lVyfQ#XCh=mS3VhnbD<hz<!g{>=^N=RTcm>dq#1X+0X+
zbn01?JExqrX3>&~ZKsVsp!J8F&uxwj33dF<t@p2&=I%^3)sbAgYbTM=rta+Ynq>3o
z@z$=H^G6?WOnLFe?<^V;YW}-BS)OR`+NJ%X(5B?np5~6m1(O>yt*b^K5IObs&z}<>
z5=s`@FaLW^BDY4<@%CN12yL2p@|2nti`JZ&UbXhDTo0f8;jxvu%@q!Q8_K-^00000
z000000000000000000000000000000007`$#D3w@P+7R7ysRu#Tt8;Qy@zeB%qMTy
z$HM{u000000000000000000000000000000000000Dyh0B7fMhj|Tw&0000000000
z0000000000000000000000000008?~W$qmgo(<(*0000000000000000000000000
z00000000000002+ui(INN$AL;vQS5AZL+;RJ~owJ7J6SrE+ZWLK9qX_0000000000
z0000000000000000000000000006+hjv<jF%R5qQlkM&Cv8nX3%3RXW^FAC39t=Jj
zoE3~(|LgTz)_1P2S@+z!&#YUs?)Y`l^B&A)0{{R30000000000000000000000000
z000000002{%(S6HLg7$(xF$5Tq-11SSy6Ouswvr?h|Nf)+B)Ltw#>Lt@w7XOA0M*1
zKkO(JXe_81lCK^v5A{`#6y=K7kE!4K+|vGV`t<HIm(`y<yJ6wN#FWY*xxT_>NA?vj
zEn1jpPIP8ti{r~Oy)`et?4m-r<=OkQnbyS(b#-lRr>$<AI=Hvy$iA8-k-4c%Hg<Y4
zlguVsdaJJM_-bzxvzGTPI6X5Xl}s*ZTYk!<L4~T}xAavlE{#lYN_A&rOZvK8{l(3_
z;UkZqKBFZ&y=h*0R$^}Ju@$}b`n!yl7R^p%V-20FQthky`&^w}&>LQL&g7GmYueAM
zNv&91KlS+XLf!J({&^`qu{)DZbtKm&VkafjiPqHWzWP(Y&>Jp&d1`Y!amE=dPij~=
zdDaPKy{9{_|FV>xoM`Wg#XDPKi@UR_bTZ!FH~5~XxAuqiUG+=nt!SIHwxy+MT4~|J
zgh%%`P*gOx_ixef*niJYU+$lSv$BgPEM7ikMf>XI9mkdA`Y#Wc_g9bP&ODp!TozlD
zji<Lwc+dRatK4(_$(gA$mn~S7T9K}6pZeC~-ZizY@5ub@@<e)vB~?FW-1YbLPW!4!
z!@4_Lb4#kFu%zmuy|oVxh5AOGUmOE#<il((t+CIvNTJ5I$u8Q~QEYK4)!r0O@7Q5(
zon(8{3)PP;DpW74-nIJNL}&Mob#n{4BbV6Ub#-`fu5MXbWnr*II~Pr-QrZ5ZmHF+q
zVIOaI000000000000000000000000000000000000002k$13uN4f}Wy0000000000
z000000000000000000000000000026k5%U0;o$jD?gan<00000000000000000000
z0000000000000000RJk6h9e_G9jUdAZHYBKsdP)GGM8AAdNg!i<(gmh+}V9aW@Rn{
z00000000000000000000000000000000030N-o^Lv@Eo7|M#svykvPc+m$)Cwzj9I
zXKY7mZL+;RJ~owJR@;$iNyclF9m{H4lj%fjYISXEd{weJ)j77MdD5iu6YD0{HMLG0
zn@icWVBYM?iZ@r}+O2FjdFH}gxL^KVR+2j&{^DQbp-|Cl4Kt@Np7Q=J53D=&yDR^#
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
zJtDy^q2STr?py!>000000000000000000000000000000000000095!sv?ol#Ew)`
zvOQ7TluEUA#M5n=g{f4wswkJSpgrE4$kZ;%rDkXJ<qi)=LJj>z7o<~NiF7uZE7X$6
zG^dkY*<`A-BG+&v_*5u(I`~E|000000000000000000000000000000000000001h
ze|W<p6T=;;reu4fwkefr>xie@G7D3wY-D((p}aq3K|0lyNN1CYOl?ad)0|FrWs|AS
zeANZ*@#b9WqFkx$jK0!EmHCD@?Bf#y00000000000000000000000000000000000
z004k}tTOkG1|vej<-zh`TJ8e?00000000000000000000000000000000000008Vk
zRdi4&91aaF3ztWuLnGs&#g89wPX1jTDP#;U3x`WeDvK5-niHMb#qnjCXe3lzKc;@m
z$G7%}#}pNc9$6?lxM*%FlRZ6|NoEr*ePus%cC0^K5gr^0mxng?7LH7BX^AzZQf(da
zbXz8tO~taylbKj+s=XzV&POweL@p`O5o<|iy4vGw5-qV*XFer1V_U7osZ@JYJl)q<
z&#;^N!^+(G4Ow?qD0ncqG3W_S4MwbgZT$o5uU_A@zHWVJ-M#BRoXZ3N0000000000
z00000000000000000000000000Qes{c1W~#ZdFHWZR7G}CYwsHS(X2L@0nc^8oS!#
z&54$HHj(UXNvs|h%l#W)8Ld6>z`vnxYdY1@Uv2#0zp>K5zQzuU9(~*)`7+!7#QYAC
z#$-0p*TaNZeXPE&B6{=*dn?}+PbWIF{WZqN#vNN8J$lg__0S&AWIIwV$<}0|Wk7G^
z%c4g&9K4sh%es>-joqEemEDQ{YU4(gMu#6)n1{m5X10H7Y>lr<Hm5oZMW@8-N0vl~
zPuzXU<*7`zuh@j6ilf6P@4i@JMw?UJo!MNu>S%QMv^{j3PBbSvo7eO;Ieu&;I(+u-
zi{_`I(CoI$lxuoSQFQo;yDz`<6ikVYs}ApRAl>Qq#^v$M^8TWg`K7vHA8!=^00000
z000000000000000000000000000000007v>D)NU7`*;ul00000000000000000000
z000000000000000006L$Rpj1f!H+`0tHB?G7lPjezX~1?ev(T7000000000000000
z00000000000000000000004mh<%%QWaA{foSX`VxMx*&-B$7WC6-6TD<<UqeT$(?W
zROHH+1wEnQmEfh|@!;Oz^TD;j`*H~Y00000000000000000000000000000000000
z008j+-a(O?@UnDjY`ncaxi-}?wx_3OY}xJ-i+7h8-Cbg2cZo#@Mrz7;X)`i3JhrrO
zEUCy1rYv|Q6#OOlQ}9CYT<}crWbjxn0RR91000000000000000000000000000000
z00000{ux9g<>9huq%2&TKa}JT#rZ=te~9D{MUhBZc}1>hS@6A3@M`eK;CI2VgC~QZ
z1rO#D000000000000000000000000000000000000002sf31>8Q8=`zxHpLQ29e&N
zs5nwop394b!%bz;NLjcve<-QQ)hY{q5DNYh{3&=LcrJJ*crth_mjD0&0000000000
z00000000000000000000000000RNi}j+BH$#r0z*+<Vy8vS=g}F3lfG@`vL5A(}r#
z@`s{GBvf9Js}c@g2<2V?00000000000000000000000000000000000008hWq%s;V
zDK85Z*N>TS?_pajaw*~9FQMEE000000000000000000000000000000000000002~
zB}F1-<rTRw9K00Dy#N3J00000000000000000000000000000000000;9tt1NJ)99
zxPHupdk@=Mk&6!vJ{k&M30@3-6Fe0>7CaQ(AABeHdhq4ow&2sjjlt$zHUIzs00000
z000000000000000000000000000000>>DM~aCx}2vJjLEE(FDc3PH4@5JbugK~Y(8
zv^-a&BpM2bBSQ*7(a_$Yv^Oa34Whk4q&Fxk&IRQ~6}hg423LfFSA#zU&j-&0PXs>+
z9tgf0+#P%+_<V3{a8s~3mkj^_000000000000000000000000000000000000Q*E~
zG#n0>Mh5kUMHMB{aCx}2JQtLOOUnweNJ$|U8C)n+R8(3>EgDitEvhVumWRX9p@kq)
zTnLJy#nJLy#oRGhA{s3#E3e4)H8iLW1uq1@41OGZJNQztCAcQIG<a8#4HCiW!R%m4
zE*k&<000000000000000000000000000000000000D29HhD*Ywq2l^6^;@33zp@Z5
zDMT-ywR~_PT3m>(e&q2%g=n-ldeu1<g=nNVy7cApLbRw5?Rk1@S!J}mB-|Uj{^XL%
zXeb|>c4zV9Lsk#%i{<jRKDTtp4zZ%axmbCBY<t$aj;}_x$5wxFbLsZjs!79&x5dU?
zf6wM<Mefwfg6l)UAA)Ct$AbrhdxATI&*l;U000000000000000000000000000000
z00000007`0&EQB$I23wm!je-S98=mKne@|}KT=W=DGrZ5;6s0y*;w5BvGs?W&yDte
zJf^((;z;kugPuL~+OqOUarx*2BA@)>vAv0rQ*Zx#QAMu#vS3Xp_+9Yx;CsPWf=>lk
z1sCTM000000000000000000000000000000000000002s|A+k|Bg5U<={3pb)8nmO
zGv`P03GFkM&KzI2VAh(t>1&Gf3CXEF%^i&kCO2kUR~6+GTISR*JgH&rs@7%oon`ri
zGbS#{%$(Uef8K%>HKqB4`3<ejnN#O47(Zw2_>z3Wsb@{@oO0HhMN1~OofaJ$8CgE@
z<S8{P7Ogoky=v`Q6}h1d4T?g+^TA`meZiN5TY@WtO~LA5d2mK>QZO+X5geGy1^@s6
z0000000000000000000000000000000002spWc3vk>Q>Nr)OrQlF0>a%TF1cPw4J!
zO|~am8uN<t3E52R;)c4qwzkt&w^bGrQmOW)c)Cy`Qb>p|%QW_h<`XhgXD(Z?D77M8
z*FLpmXk=u0I+e=ym&h0BNHyg;>5UI9BqTb!`wJB16VA#mp0IfNlojo(mv@xr6HcGe
zlAYc(FFh+Uw{=iHVfys$Gndt$JiB4x!o-k#!qnz?;*2v^p46~#@~rZF!sL^aYueAM
zNv&91KeZyCP~TO*bl!@#No!kLn#wBjQ?OwlUjhIC0000000000000000000000000
z00000000000NBSW@`nxkcn|;p000000000000000000000000000000000000I-i$
z<lfQX7op&l;DzAVxeovU000000000000000000000000000000000000Ps(#G#U<v
Xheo1>u(Bvp2nQDx6-UG6<&pmj)UnnT
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/tests/migration/test_current_from_v35.js
@@ -0,0 +1,29 @@
+add_task(function* setup() {
+  yield setupPlacesDatabase("places_v35.sqlite");
+});
+
+add_task(function* database_is_valid() {
+  // Accessing the database for the first time triggers migration.
+  Assert.equal(PlacesUtils.history.databaseStatus,
+               PlacesUtils.history.DATABASE_STATUS_UPGRADED);
+
+  let db = yield PlacesUtils.promiseDBConnection();
+  Assert.equal((yield db.getSchemaVersion()), CURRENT_SCHEMA_VERSION);
+});
+
+add_task(function* test_sync_fields() {
+  let db = yield PlacesUtils.promiseDBConnection();
+  let syncFields = yield db.executeCached(`
+    SELECT guid, syncChangeCounter, syncStatus
+    FROM moz_bookmarks`);
+  for (let row of syncFields) {
+    let syncStatus = row.getResultByName("syncStatus");
+    if (syncStatus !== Ci.nsINavBookmarksService.SYNC_STATUS_UNKNOWN) {
+      ok(false, `Wrong sync status for migrated item ${row.getResultByName("guid")}`);
+    }
+    let syncChangeCounter = row.getResultByName("syncChangeCounter");
+    if (syncChangeCounter !== 1) {
+      ok(false, `Wrong change counter for migrated item ${row.getResultByName("guid")}`);
+    }
+  }
+});
--- a/toolkit/components/places/tests/migration/xpcshell.ini
+++ b/toolkit/components/places/tests/migration/xpcshell.ini
@@ -17,19 +17,21 @@ support-files =
   places_v27.sqlite
   places_v28.sqlite
   places_v30.sqlite
   places_v31.sqlite
   places_v32.sqlite
   places_v33.sqlite
   places_v34.sqlite
   places_v35.sqlite
+  places_v36.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]
 [test_current_from_v27.js]
 [test_current_from_v31.js]
 [test_current_from_v34.js]
+[test_current_from_v35.js]