Bug 1039200 Part1 - Add migrations and associated test for tags and tags relation table, r?mak draft
authormilindl <i.milind.luthra@gmail.com>
Fri, 02 Jun 2017 15:46:46 +0530
changeset 607279 cd5798c8ec86b93616e1a6fecf0f65e52661a722
parent 603312 a3b192dc8344679ce208af42b6246c3c0d42cab3
child 607280 d1dd7f2d3d2a56efd181b36675f0d273649f030c
child 607526 3d3057faa23339f5933da73d14aea84729a3c465
push id67956
push userbmo:i.milind.luthra@gmail.com
push dateWed, 12 Jul 2017 06:34:20 +0000
reviewersmak
bugs1039200
milestone56.0a1
Bug 1039200 Part1 - Add migrations and associated test for tags and tags relation table, r?mak This adds migration for schema v39. In this we add 2 tables, moz_tags and moz_tags_relation, and get all the folders/tag-bookmarks over to them respectively. MozReview-Commit-ID: CcUWLxCwTaK
toolkit/components/places/Database.cpp
toolkit/components/places/Database.h
toolkit/components/places/nsPlacesTables.h
toolkit/components/places/tests/head_common.js
toolkit/components/places/tests/migration/places_v39.sqlite
toolkit/components/places/tests/migration/test_current_from_v39.js
toolkit/components/places/tests/migration/xpcshell.ini
--- a/toolkit/components/places/Database.cpp
+++ b/toolkit/components/places/Database.cpp
@@ -1100,17 +1100,23 @@ Database::InitSchema(bool* aDatabaseMigr
       }
 
       // Firefox 55 uses schema version 37.
 
       if (currentSchemaVersion < 38) {
         rv = MigrateV38Up();
         NS_ENSURE_SUCCESS(rv, rv);
       }
-      // Firefox 56 uses schema version 38.
+
+      if (currentSchemaVersion < 39) {
+        rv = MigrateV39Up();
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+
+      // Firefox 56 uses schema version 39.
 
       // 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));
     }
@@ -1183,16 +1189,22 @@ Database::InitSchema(bool* aDatabaseMigr
     NS_ENSURE_SUCCESS(rv, rv);
 
     // moz_items_annos.
     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_ITEMS_ANNOS);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_ITEMSANNOS_PLACEATTRIBUTE);
     NS_ENSURE_SUCCESS(rv, rv);
 
+    // moz_tags and moz_tags_relation
+    rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_TAGS);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_TAGS_RELATION);
+    NS_ENSURE_SUCCESS(rv, rv);
+
     // Initialize the bookmark roots in the new DB.
     rv = CreateBookmarkRoots();
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Set the schema version to the current one.
   rv = mMainConn->SetSchemaVersion(DATABASE_SCHEMA_VERSION);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -2319,16 +2331,61 @@ Database::MigrateV38Up()
     ));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 nsresult
+Database::MigrateV39Up()
+{
+  // Create moz_tags table if it does not exist
+  bool tableExists = false;
+  nsresult rv = mMainConn->TableExists(NS_LITERAL_CSTRING("moz_tags"),
+                                       &tableExists);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!tableExists) {
+    rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_TAGS);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // Create moz_tags_relation table if it does not exist
+  tableExists = false;
+  rv = mMainConn->TableExists(NS_LITERAL_CSTRING("moz_tags_relation"),
+                              &tableExists);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!tableExists) {
+    rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_TAGS_RELATION);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // Migrate old tags to new tags database
+  // DOUBT: is there a better way to do this? It was using moz_bookmarks_root, but
+  // we don't have it any more. Is using the tags folder guid directly OK?
+  rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT OR IGNORE INTO moz_tags (tag) "
+      "SELECT c.title FROM moz_bookmarks c "
+      "JOIN moz_bookmarks p ON c.parent= p.id "
+      "WHERE p.guid = 'tags________'"));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Create the new relations for the tags
+  rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT OR IGNORE INTO moz_tags_relation (tag_id, place_id) "
+      "SELECT (SELECT id FROM moz_tags WHERE tag = t.title), b.fk "
+      "FROM moz_bookmarks b "
+      "JOIN moz_bookmarks t ON t.id = b.parent "
+      "JOIN moz_bookmarks p ON p.id = t.parent AND p.guid = 'tags________'"));
+  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 38
+#define DATABASE_SCHEMA_VERSION 39
 
 // 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.
@@ -294,16 +294,17 @@ protected:
   nsresult MigrateV31Up();
   nsresult MigrateV32Up();
   nsresult MigrateV33Up();
   nsresult MigrateV34Up();
   nsresult MigrateV35Up();
   nsresult MigrateV36Up();
   nsresult MigrateV37Up();
   nsresult MigrateV38Up();
+  nsresult MigrateV39Up();
 
   nsresult UpdateBookmarkRootTitles();
 
   friend class ConnectionShutdownBlocker;
 
   int64_t CreateMobileRoot();
   nsresult GetItemsWithAnno(const nsACString& aAnnoName, int32_t aItemType,
                             nsTArray<int64_t>& aItemIds);
--- a/toolkit/components/places/nsPlacesTables.h
+++ b/toolkit/components/places/nsPlacesTables.h
@@ -208,9 +208,24 @@
     "page_id INTEGER NOT NULL, " \
     "icon_id INTEGER NOT NULL, " \
     "PRIMARY KEY (page_id, icon_id), " \
     "FOREIGN KEY (page_id) REFERENCES moz_pages_w_icons ON DELETE CASCADE, " \
     "FOREIGN KEY (icon_id) REFERENCES moz_icons ON DELETE CASCADE " \
   ") WITHOUT ROWID " \
 )
 
+#define CREATE_MOZ_TAGS NS_LITERAL_CSTRING( \
+  "CREATE TABLE moz_tags (" \
+  " id INTEGER PRIMARY KEY " \
+  ", tag TEXT NOT NULL UNIQUE" \
+  ")" \
+)
+
+#define CREATE_MOZ_TAGS_RELATION NS_LITERAL_CSTRING( \
+  "CREATE TABLE moz_tags_relation (" \
+  " tag_id INTEGER NOT NULL" \
+  ", place_id INTEGER NOT NULL" \
+  ", PRIMARY KEY (tag_id, place_id)" \
+  ") WITHOUT ROWID" \
+)
+
 #endif // __nsPlacesTables_h__
--- a/toolkit/components/places/tests/head_common.js
+++ b/toolkit/components/places/tests/head_common.js
@@ -1,17 +1,17 @@
 /* -*- 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/. */
 
 // It is expected that the test files importing this file define Cu etc.
 /* global Cu, Ci, Cc, Cr */
 
-const CURRENT_SCHEMA_VERSION = 38;
+const CURRENT_SCHEMA_VERSION = 39;
 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;
copy from toolkit/components/places/tests/migration/places_v37.sqlite
copy to toolkit/components/places/tests/migration/places_v39.sqlite
index 5df92ec55c47a6c872d3899641d535bea7484c91..e74ee62cc0fe29184b8eab144d1ca48a5cb3d37f
GIT binary patch
literal 1114112
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
zlZ_X$!MY>%gOz{!VC=)AvQO%kT33%7%0{i>HX7E<quG!?>d5(U?}f(E#@QoXt@@~6
z`u>I2|C-$=4N50gpHnEIM(Y<(VZ)L02`v_lC+nkX!WjKX(?GgQX;00c?mvI9^20yB
z^7rR$K50^_TRr&tkKM4E)Y9=#R#VnI1f~01@<VD}rcvn(j!hq|ociLsFZuM36{mQY
zB>qKwO?+1Th5ix%00000000000000000000000000000000000000000000000000
z000000D%8$Dx7Y~W^;Kxj%vv{^z!9v_e@nf%#h6^SD7L0u|8*|ycn}rnz8hQcQ`BT
zZpmZBD$G!<e#Ty5D-GqJdM;!48MEAbgLer4000000000000000000000000000000
z00000000000000000000000000Kg4aW+eCC;6(rc0000000000000000000000000
z0000000000000000000000000000000C0o(^gp+Fog`ime=R;J?iVTX>v|CY00000
z000000000000000000000000000000000000000000000000000Dym&U2d1uVE0I=
zL@=pn^)u>xJdp_2tLdn8f2GSM)#!CI@lYtP)rZv?>Hb|#t9>vMQInaK?QeFtq*lEy
zqRc7@HKn9iR_~TwQnOwi%0{i|irRqPB^@_v6Kb?R8IQ)aM1Ez9Dw|8%XZK1aLHF}z
z`X1aOB8lh4Q{rRd55=3sCA|m$0000000000000000000000000000000000000000
z0000000000000000Kk8^?Q(-Hl?Wykt$s$Gk0%nrdNmz&Z;|(TOUhg18rw`f6pCy0
zVRgo}Q*O0IlvyRArj&GPKgSNa*%r!1tw2eIyjwnQ3nkQOeKH=6X^DJkrQKLWFcMLd
znbNS+mwS^`lN*r57sY=U=ftgr|5NyjLauOl;d=`oSO_lMeEA!f-*$Q8@}5iIzVyhY
zH(fe&Y3KYu&%bXzKY#c9uKY9k!TiT^1NlJit+{JA`ji0x00000000000000000000
z000000000000000000000000000000008)}s&&s6$!oKDY9z_!+V8dbohMawdM22j
z4jh%7LodJMv3*yK{GG0{z+_$WygD1r&uJa!%5qmFmn1a=wn#R~=H0f~-R{tWQM2Pa
zz4BMA+<i4Zd|@oBp6@u;I?!X!zfo#<_3P7H*VR}{l!{9X6vr**jgbqT@o0EBs+?)C
z*X=c%dzYH;Hk)~V5-8@UGxOnd$CTz=>-c%OVYj&h&x##1m|HJ=@<_3ZcC>CT*c#2o
z<6587T3P+8-$dQbD|fJcw>AE!cNFtBHQao6>OwA^?i{zb++^)wZ?XB(dt$EZ?Y|Sv
znf(qu{)K^LTkq`X(f<5+M~kC=m)Xx&oTl7wH`?W%ed2vq{^aj8_9#v2cz;7jxT#C7
z+qv?jw)(ARSFaZHfx%8?a%8-1OuZPD>vpW%#g@h9pZZEMKhrnR(KI=f89zOAQT`Rb
zd9bzv#e=Pq+Y^aEaVr_C)1xmImv}6kO-%$-eS^s(Y1h&1)>ew|im%E(8jLRX`Glv~
zw<Ds46SG|d9d#pVxn<kRW7}MHN}Wq4)L>+>-$w(*{zsCXb*awgxrVOsX1S@_+Sg5s
z{Wevdj%PGAowr&)bgbB0%jH_ea_!xTQ^A;AS7mHZUs-W`POI5_V#UR^bTo!q#+9Sz
z`*RWZp-SsUSv)+iY`!(w7p{4V`Se^gcj{tdA~=|BI96dTY2V`DW%gkutR%HS@es1l
zJ{Yq)ek(RQ*&I68bg_GOP@A{6m0LacFZT4x{c1)FoQ-GVni4U)JXQT<afoB#p3_sw
z$ZW2mamaqxR_n}b&8{As$Kx6@e*-<xZ~gqZ+3Sh%p<+I9ZmuQLtM>Oq=ez8uwphK}
z7JIur&QtMBIGf1?2CN@952(pN&v1v;d+1&(AB}}aTSF~%qluGuk4)Ns)n|=Tw>XN+
z<1j9oyqZl1!s%cprdZp5qVEri<77Ky;l`$xcvsta&VF*U)xUYMztbZZXCGBEnvxET
zs%m0!jME<}j@91Wel~ipw=H|Fwcp;o$r|It;usE(eenPSr;0!C9vD!xIW;|P4RGqo
z;(*h=jm_utvEFbZIU>KV%(@^O7YC9(#c@t%qk++MFg&fKt)6%N#L8dy&Dq51I%O`M
zZ$3NNEw_8EceA)1yQeFjR;JY4(wQ2+9&dGRD=!Xp_ucKy(Ydxr=2XiWd$-5B4U4<6
z+wH5z7*;a+`G$)}+kAWR4L&namr72w_GVf;x;)3+`dai0YJYJz-TIZ$lblr(vx^tj
z6LsUo5$1Ds(-Wy<O=py&llG%7>sHyhc<tLAGioTFP|U^;2Lr|2icXsDa$0>37H=<k
zP>bp3zT)wbeR$UwiXT$h<dohyVs`d4IIP}F5A~&|N3o0X@uWZKGn0$F$7QpN_i(Yx
z(oE(h6AGqFyBjx=W`)h}&O7a9cTerw?)^$KyP~gtv(M<U;?&MVHlwe{v-Bt_ZEZYJ
z(yFQzTibocC(6CA{=)|V000000000000000000000000000000000000000000000
z00000000000D#w`G9$V71}_2t0000000000000000000000000000000000000000
z0000000000000000Dv3Jr~lc-3zGOZJplj!000000000000000000000000000000
z0000000000000000000000000;I+?dcS=<@X(p0%cx+Co!X}00<ubj(E?$zvPxS-<
z00000000000000000000000000000000000000000000000000000000DxZ_Zn?@f
z6G_^ga)mcE@6#hT@qJ1E4*&oF0000000000000000000000000000000000000000
z00000000000001hUlQdudB1H&ou8am@^flBlCk^rs7J_>cuqVaJ}7=$ToB#jc0B+9
z00000000000000000000000000000000000000000000000000000000QhCI&m&9y
zAyu8838trq(rQXcYjGuWIH3k3J;{-HL<t4cH@ju2eQda=q&^%Arh{QkNslO6jZ2n#
zh7!TBk{L`#gUR^3l0Llj>P%Ki=WlV!QtyUMy3|BON#Ep<rQ<2H&0!@MPGloWS6ZDp
z84OQnQ+`>Nnymp2kLYXFPA;yw#;#Ak&ITe%CY+9^w78n|+w{IOYABvi4i{TyhE-K7
z(+|KS9FllW{FQjOxFYTm4$&oQZ}gi200000000000000000000000000000000000
z00000000000000000000008*UcdOiQ3#sb#OfWq?lvYzpT8k^0!wEGQ=}C^nBT6Wk
z_Ux9A+fs>OSg%lW;Y2o~bfwjqlfm$GHs$ck&E6R`6i+CJivwkbRaLu1?pbf-h>{7X
z<0&n!ChdFVcH7u+Pf2Y!7EA}jnvxz-G`BIrkU8yOIvPyI=aux~rB`RNN;>b_C--jL
zq)Sail(ch)+`QQ4@QA(`?d0O3WS_o&yLdzrU)K`=0000000000000000000000000
z000000000000000000000000000000004fW?6a3iHk;pLv$<SV?y=FXx?>Jmat^(`
z@SFd)yqNE^EjOxO(df*nR5ADIqg7x2x+I<#3u0K@E<A<r6#iG?j|;iNc%i9Kz3{_@
zCl~%;A-B-GaPacaE`R;<Cofmr=-mSV00000000000000000000000000000000000
z00000000000000000000_;=e=ZT$ZER*%i=a_w?7IE<e>&prFZ`-*vel~G@3{yzFH
z*I05Yo=_quRdsqMn4Zp9Kb2nEeoy7{csrNIyLz>lAFMFy518ZabS(|kuOzd@!Im5E
zDPJCJ$I@V*`bsgc-@3fG9m|7_s%jz>Oc%#nZhyxXqy2ufz2DWINUYh&qc0ZoJ-+3+
z{Y!I~)Nfv{-@Z=$Et`z`ZRT9tU8BM1dUKR5kFjlOj8Axq`2nv{zt<dNo9mQ1mrSU^
z$kIT|4flAK2dZ8g=%ayRzTa)s-(>yDz3X&5qp9h9ahQjW6`SpGEe})WY+W4Y{X@lk
zkF%tHX`1Ew+Z{%IjX6z)JgkJ3q_({C><izD74urzXt~d9SuXdh8SQL56W5f;V$)|I
zj1}_>_Dy=zD^^?C-X4hrLdCZ&ps4{Z7S9Bx)I>x{8^w&G=pkh$5Q%3}iC|ud1k|Jv
z2`s(YffW}>_KDh5G2dp>m*q7!=y13i9NCA10pri7-zdI?f+YS{yjy6ZSL`eNxbS%4
zHwvSLTNi$?@RxcO00000000000000000000000000000000000000000000000000
z00000008_axYOr2-0z=J=O<&<57*BcKNX)@6PQdTf?*{R)RcHKqU4$bM*|Iwn;nNc
zs$NCkskAy%?AEyHRdy<wtD(Nkao|^W8f}(-oqk1OGOj7s42_NY%8nlKIu4w;Ui(xq
ztt7Q#kH$d5ogT-5kyn}_5zJ^aY9u}tS0W{|HM$)KdbVAsZ!{Z^OlFhui&>@Et>F%r
zqvp8T$voN2@~g>gIx!gwW@2XBqelV_w>cd(&Fi;~sTr-<?&txBqo#HJcIKgm)ofDJ
z8@ApdZ#el>S_v!3aK1Qb(_y=#rhEOi#(9_nFTGbrhdP_1repmMs}I81T$z3q`U@{g
z;z#1U;+x{j;&bBD;^X4|;+^96#BUZ}Ec{Hk#3hjwK`|=2#hs!~1Vq)1zHa~k00000
z000000000000000000000000000000000000000000000000000RO+rWT!2C+ogYe
z(*u6%<u~5r`|9@1vdbn(?|M()dw=V*+pNM5o1XsocYj=7EPUu--NT!Ug%1qe)4Ziv
z5Z`|PKW!=&#_s;|p|WD3<?HW%o3~gva$xrzTZ@I-=YMy=SDf^rz~dirS&O;#uf#1C
z#><11U;6%q*UQ$+5C8ni-`lO1fBInT!w&1^13x-7S#7<%|H$c1r}Z*fR{i-(>*a;&
z7ay(i%1-aW%Be5D`;rwLSlD)}$9j2UV)q^HGW|^ag^x+%XX4+)HSs<1oOnijqwr<%
z58`jd)xr~nkBL7QZxI>sYoc2m6*mh{;k$*e-RO4_0000000000000000000000000
z000000000000000000000000000000005vXm-pMEX|+C>NW|yWnR@@~z;=f$*|u$!
z_t|FDaJ?1}h2k^y?wjQkD_Z3fS}Yn*)<@MusNP;J*R80Xo13ezUN2a+Ua+!CuJf)A
z`sKzI4K*bhOr+{PtK*rZ8i@q!ox9|=74fu^QWMIo5~+9Wlv`I+nRzrDs+U*qy}~JP
zwbhrIsn<A%awFMlBwLKcXC#}AWRsD&%Jc*Ei?>SR=i<lWCGjufJK|aKwD_v{lK30(
zIq?~BReVxBB0ehKCLR!P6(17s6@McB=tf@x000000000000000000000000000000
z0000000000000000000000000004m3G>7c5`Hf_|k!*9yHk-Y|A$x7rMp9)Yl}1gu
zk!&@REk@!qlFdf4$w<nK#A_rTBXJvv%SfC?;xH1~NbIug@s^4EB)^!pi=T;q6W7G|
z#B<^q@eT16@kQ~J_^kM}cvO5;d_erE_#^TAA}#V_TKu+n(~UkT000000000000000
z0000000000000000000000000000000000000000006)*Fqdq%N%#A$Y@3yBH*1_`
zo%{I;Gx%_|Rp+rXx0Si9%xPr~E0e9v?v(9by-v2-7OISNXQe~-*vgG$tC4Im5}%Q5
zHj+(7Qf4IHGJSf#NJ`@8;>Y48@h{>#;#u*u_^SAl_#5##@fmSdd{R6jJ}Pn|Ba-6J
z#M{K1#lnp}NB{r;00000000000000000000000000000000000000000000000000
z0002MFFJ=T+5ASb-AJ}=mR&Z<IrQ@7Yxhi5FBP=M`l^-+=?CwqTq?xsXDW=2<wmmA
zNVXV>&qy{K$tEKyGZL?nc#OntBrYRy8i~V5WFxW5vg9q(SLH9vOX3IOY4LgSDe)ok
z4)Nx~yvU1~I9K>1(IuM2t)i;%7GW=3D}1x?H--OMc<4r-BLDyZ000000000000000
z000000000000000000000000000000000000002ImXyo;Z6~9ra*e~|=hMT9F24~t
zml$a3YU+<og(hR$jX>Y%d9_iCs#C4U#<m%O+}W;huC*^18Ok(lH3Hd8^js)C-`3nR
zo2WDb+T37jWTLmLd+uD`X$Gc}1O3Ok<|Em*LAMczw@x&iooH=Js6%&eG6Lswoyx`L
z(72Kt87VUYXHUoKdQUc9$Oh|<IE=vPY}br7ujNN$`8KZ+7&#yAz0f$?ID4e4RW?_e
zZ5xd@j}Dz2Yip}E0=*-HeUrIhPws5@QI8SmJ{Ho>sG0u8$wX6y5$HHKIh;M+p<OsX
z&|75$+9O@9@leZbU(du5ml0^oPX}91kEY^h=1!GwmG^sF@<VD}rcvn(j!hd4nkQR(
zV@H#{r@OmOZ7~8zk4^NR%+F7bA3K%u8G$3Q&`j>kXk42=pWAE(ng-HcN_%SlbpLsK
znf^{V#Uqk<NjxjQBtE0R1ONa40000000000000000000000000000000000000000
z00000000000002s7sM8a&1I{WoI@{PzIM-)(=1k*MeVUZhgqyNi|GgNkj-L+S&Y@s
z*nN7@YZmiQJ(nrdM{|mQmc-A+kHt0dg8mW!000000000000000000000000000000
z00000000000000000000000000D#vfr);x1oJJ};jMOeWWRIT89&edZEv`!9hvGT$
zHSu}<B>(^b0000000000000000000000000000000000000000000000000000000
zzi>9oE}P^WdinCTd#0RA1?{mu$5J8v;2rW(Ayz+Q-z2-dMj`*ybD1)Ij2dy1Bz`LX
zReVqUvv^wkgZP5@Yw@`FxVS|;B;G6DE&fnEAl@S0AQr@|P(@4x#d$Fz`b4MrRq;vD
zDvpR+eFy*m0000000000000000000000000000000000000000000000000000000
zz-yDsVe{JjH7<u_v)KbyX|I*-F|+MAn_2lTGuyh;%(m<>Gv9VI+q})pHdUKhS%sN-
z%gxNQ)y&*m%*^F8Gv{VAb8Ip*xy;P$UZ=yWw=|a`S6VCDZLQ%ZtK_$`Dl79?naj$Y
zR_3rW*~;urJ@eYj^o`btYDxS|{7C#jd`Emsd`<kFcuIUmJSH9y4~st*H;MO&-xK$X
zH;M(36*EE+uNP;<py(DS#4*tzYQ@{dE&31u000000000000000000000000000000
z00000000000000000000000000D#vRm&4|<1#0xnYjXt5Z1-Nb!)CMj?YqpPf2Y~T
zzr&1H$$l%hZ#QeJwwbk6)n?S=+G^#_EmrRES-HH~%I%xXUY;_um&a?CoHv=J-8Y+=
zW4D<(_n39E+bqfDW`DcgV@B<jX4GEca(HbvXO)>bTxKRa&CKp_I=p&+J=GgJ93GFi
zOiV~Mg$h}`B)%>FUOXxOQamKyC4RTiU-<RH;X*+4in~Oe*dw+Isc^0EOyTbePZT~;
z`18Ws3cp#%7cLZjRoo{sB2uWh(eD}n00000000000000000000000000000000000
z000000000000000000000Q?8r;;^}Fe#trX@;klmt2I7{<gyh@0Z~$vzv2*P<LyTI
z9fuzO!c9xH?|5wARcA?K&rbro&9>!6G573~zq89MZncV6ukJL9Tdd-zzOutC`mExk
zFK#!Bo2}v}Jlo9TCad_-K($#cGm6<4z7?x5i(a$%?1Qm#v*<C4PmB+3HH&VmI2zkx
z7F||x=w6>$bXvu}KiF&*9ai!5M>d&7*($#7n`LIvZWZtPiPz`wTHDP&(R{mGKgvyO
zk5oU1y(L9gSzu4e85Tah<L0G9%RT$V`}`%PM_;Vkte3o2={>Qfs_YBbJe4a-_T|!3
z)lbUHCH(+A%cX~d0auy6zM6$ENa6?LY4LgSDe*z^c2OyEA}Y>`PO-0WPS^@B7XGR5
zm}n||Nc>je9fda+@`YHTyYRmYErtDs>VmxR(!#g&ApigX00000000000000000000
z0000000000000000000000000000000002szm#%$zimEOH$9O$)^tWWI_Wk7vx(Dn
z%3M0%e0H$A%m`$YQ}KinnKU@fKxZu6*whm5Y8%h_jDVKQwT$K3yA!8^v1&7*s)<lA
zZFZ5(Krot_T;$j)@AsxvRVxOJ1_Q~q-r3Qk{rT~ZmYa;gj2hBswHmn0fRfA>d)SS@
z^jtJ|>SAIdIGAmyGy}bj&FAy6-f$v0vdIX9M_WTJb)$)scaKbNF#;3k=2{}XYJX33
zzH6(wuA_Bx!PaOt9@qMI8-dZu=FqvOi`}z>+I)o(I5SX}N=~%)W?DPCJZ7M8prdJW
zC^LR~=;96|(58l)?@nFFrPH0`H=BDI>{KR4#@oi!i_x7%V6rZGUY(8R=d_M<dyK%{
zcegi3=h`BfQ!Qt_M&NX2K78(&(wu7@Kfl)qbVSr}Vzz6bqi!U<-3YWdx1Wuk>ut-P
zYwh1`1meRN#<J@9j$^F@Jpm(dESybE1XF#3$s=jM73euVm5j{h8XAYnjX+CBW2j|Z
zIeNZ77pXD=jgbqT@o0EBs+?)4F#|nHlRDnt&=GFx+GPZeBs=R;oy~I%UE|H$%Jgf-
zF8);#Kh+Ze0000000000000000000000000000000000000000000000000000000
z004e@xEwag=CC{U%qy4a0lT;+iJ#~R00000000000000000000000000000000000
z000000000000000000000002LEL;wo&0Fnu>zS>}Z7<V9)gmN`pNN;lcf~W}YvPOI
zbK-IFm*OwP2gIL<w~GhFo5g)%UZlkZ5fbOc@QvOA0000000000000000000000000
z000000000000000000000000000000007_@k=tRj*><_AtlUv;<~v;#R_>@Y^Bu0O
zR_-V_bH8hgl{<W9zTLIS${m}{e4EQ_<&H8luXeeu+~G0vDyPfJ9ZoZ^kR4`TF3V1b
M&Fi&lWxM_V0STFe$^ZZW
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/tests/migration/test_current_from_v39.js
@@ -0,0 +1,58 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+add_task(async function setup() {
+  await setupPlacesDatabase("places_v39.sqlite");
+});
+
+add_task(async function initial_state() {
+  let path = OS.Path.join(OS.Constants.Path.profileDir, DB_FILENAME);
+  let db = await Sqlite.openConnection({ path });
+
+  // Make sure the tables don't exist here already.
+  Assert.ok(!(await db.tableExists("moz_tags")));
+  Assert.ok(!(await db.tableExists("moz_tags_relation")));
+
+  await db.close();
+});
+
+add_task(async function database_is_valid() {
+  Assert.equal(PlacesUtils.history.databaseStatus,
+               PlacesUtils.history.DATABASE_STATUS_UPGRADED);
+
+ let db = await PlacesUtils.promiseDBConnection();
+  Assert.equal((await db.getSchemaVersion()), CURRENT_SCHEMA_VERSION);
+});
+
+add_task(async function test_tables_added() {
+  let db = await PlacesUtils.promiseDBConnection();
+
+  // The attached DB has the Bookmarks added with certain tags, namely -
+  // Bookmark1 (fk: 22) - tag1.
+  // Bookmark2 (fk: 23) - tag1, tag2.
+
+  let tagIdToBookmarksFks = new Map();
+  tagIdToBookmarksFks.set(1, [22, 23]);
+  tagIdToBookmarksFks.set(2, [23]);
+
+  // Check if tags have been migrated to moz_tags
+  let rows = await db.execute("SELECT * FROM moz_tags");
+  Assert.equal(rows.length, 2);
+  Assert.equal(rows[0].getResultByName("tag"), "tag1");
+  Assert.equal(rows[1].getResultByName("tag"), "tag2");
+
+  // Check if the correct relations have been created in moz_tags_relation
+  rows = await db.execute("SELECT * FROM moz_tags_relation");
+  Assert.equal(rows.length, 3);
+  let i = 0;
+  for (let [tagId, bookmarkFks] of tagIdToBookmarksFks) {
+    for (let bookmarkFk of bookmarkFks) {
+      Assert.equal(rows[i].getResultByName("tag_id"), tagId);
+      Assert.equal(rows[i].getResultByName("place_id"), bookmarkFk);
+      i++;
+    }
+  }
+});
--- a/toolkit/components/places/tests/migration/xpcshell.ini
+++ b/toolkit/components/places/tests/migration/xpcshell.ini
@@ -19,23 +19,26 @@ support-files =
   places_v31.sqlite
   places_v32.sqlite
   places_v33.sqlite
   places_v34.sqlite
   places_v35.sqlite
   places_v36.sqlite
   places_v37.sqlite
   places_v38.sqlite
+  places_v39.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_v34_no_roots.js]
 [test_current_from_v35.js]
 [test_current_from_v36.js]
 [test_current_from_v38.js]
+[test_current_from_v39.js]