author | MilindL <i.milind.luthra@gmail.com> |
Wed, 04 Oct 2017 13:50:25 +0530 | |
changeset 700539 | df80df559ca8bfee012d4c3a01dbd8e7021ca939 |
parent 685447 | 967c95cee709756596860ed2a3e6ac06ea3a053f |
child 700540 | f40421807d8390b32979e8e3b6737f52056b0222 |
child 700542 | 24008650188fb354a08a2df22d2f2382aa84f479 |
push id | 89886 |
push user | bmo:i.milind.luthra@gmail.com |
push date | Mon, 20 Nov 2017 10:17:19 +0000 |
reviewers | mak |
bugs | 1293445 |
milestone | 58.0a1 |
--- a/toolkit/components/places/Database.cpp +++ b/toolkit/components/places/Database.cpp @@ -5,30 +5,32 @@ #include "mozilla/ArrayUtils.h" #include "mozilla/Attributes.h" #include "mozilla/DebugOnly.h" #include "mozilla/ScopeExit.h" #include "Database.h" #include "nsIAnnotationService.h" +#include "nsClassHashtable.h" #include "nsINavBookmarksService.h" #include "nsIInterfaceRequestorUtils.h" #include "nsIFile.h" #include "nsIWritablePropertyBag2.h" #include "nsNavHistory.h" #include "nsPlacesTables.h" #include "nsPlacesIndexes.h" #include "nsPlacesTriggers.h" #include "nsPlacesMacros.h" #include "nsVariant.h" #include "SQLFunctions.h" #include "Helpers.h" #include "nsFaviconService.h" +#include "nsNavHistoryQuery.h" #include "nsAppDirectoryServiceDefs.h" #include "nsDirectoryServiceUtils.h" #include "prenv.h" #include "prsystem.h" #include "nsPrintfCString.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" @@ -1061,16 +1063,23 @@ Database::InitSchema(bool* aDatabaseMigr if (currentSchemaVersion < 41) { rv = MigrateV41Up(); NS_ENSURE_SUCCESS(rv, rv); } // Firefox 58 uses schema version 41. + if (currentSchemaVersion < 42) { + rv = MigrateV42Up(); + NS_ENSURE_SUCCESS(rv, rv); + } + + // Firefox 59 uses schema version 42. + // Schema Upgrades must add migration code here. // >>> IMPORTANT! <<< // NEVER MIX UP SYNC AND ASYNC EXECUTION IN MIGRATORS, YOU MAY LOCK THE // CONNECTION AND CAUSE FURTHER STEPS TO FAIL. // In case, set a bool and do the async work in the ScopeExit guard just // before the migration steps. rv = UpdateBookmarkRootTitles(); @@ -1810,16 +1819,128 @@ Database::MigrateV41Up() { NS_ENSURE_SUCCESS(rv, rv); rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( "DROP TABLE IF EXISTS moz_favicons")); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } nsresult +Database::MigrateV42Up() +{ + MOZ_ASSERT(NS_IsMainThread()); + nsCOMPtr<mozIStorageStatement> placesQueryStmt; + nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING( + "SELECT id, url FROM moz_places " + "WHERE (url_hash BETWEEN hash(\"place\", \"prefix_lo\") AND " + "hash(\"place\", \"prefix_hi\")) AND " + "url LIKE \"place:%type=7%\"" + ), getter_AddRefs(placesQueryStmt)); + NS_ENSURE_SUCCESS(rv, rv); + + bool hasMoreQueries = false; + nsDataHashtable<nsUint32HashKey, nsCString> idURLMap; + while (NS_SUCCEEDED(rv = placesQueryStmt->ExecuteStep(&hasMoreQueries)) && hasMoreQueries) { + nsCString queryUrl; + int64_t queryId; + placesQueryStmt->GetInt64(0, &queryId); + placesQueryStmt->GetUTF8String(1, queryUrl); + idURLMap.Put((uint32_t) queryId, queryUrl); + } + + if (idURLMap.Count() == 0) { + // Quit early, otherwise the replacementParamsArray will be empty causing + // BindParameters to fail. + return NS_OK; + } + + nsNavHistory* historyService = nsNavHistory::GetHistoryService(); + nsCOMPtr<mozIStorageAsyncStatement> replacementStmt; + nsCOMPtr<mozIStorageBindingParamsArray> replacementParamsArray; + rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING( + "UPDATE moz_places SET url=:new_url," + "url_hash=hash(:new_url) " + "WHERE id=:id" + ), getter_AddRefs(replacementStmt)); + NS_ENSURE_SUCCESS(rv, rv); + rv = replacementStmt->NewBindingParamsArray(getter_AddRefs(replacementParamsArray)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<mozIStorageStatement> tagStmt; + bool hasMoreTags; + nsString tagName; + for (auto iter = idURLMap.Iter(); !iter.Done(); iter.Next()) { + nsCString queryString = iter.UserData(); + uint32_t queryId = iter.Key(); + nsCOMArray<nsNavHistoryQuery> queries; + nsCOMPtr<nsNavHistoryQueryOptions> options; + historyService->QueryStringToQueryArray(queryString, &queries, getter_AddRefs(options)); + + for (uint32_t i = 0; i < queries.Length(); i++) { + uint16_t resultType; + rv = options->GetResultType(&resultType); + NS_ENSURE_SUCCESS(rv, rv); + if (resultType != nsNavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS) { + continue; + } + + uint32_t folderCount; + int64_t* folders; + nsTArray<nsString> associatedTags; + queries[i]->GetFolders(&folderCount, &folders); + // The folder count should be exactly one for RESULTS_AS_TAG_CONTENTS. + MOZ_ASSERT(folderCount == 1); + queries[i]->SetFolders(nullptr, 0); + + rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING( + "SELECT b.title FROM moz_bookmarks b " + "JOIN moz_bookmarks h ON b.parent = h.id " + "WHERE b.id = :folder_id AND h.guid = \"tags________\"" + ), getter_AddRefs(tagStmt)); + NS_ENSURE_SUCCESS(rv, rv); + rv = tagStmt->BindInt64ByName(NS_LITERAL_CSTRING("folder_id"), folders[0]); + NS_ENSURE_SUCCESS(rv, rv); + rv = tagStmt->ExecuteStep(&hasMoreTags); + if (!hasMoreTags || NS_FAILED(rv)) { + // If the associated tag isn't found, don't rewrite the query. + continue; + } + + rv = tagStmt->GetString(0, tagName); + NS_ENSURE_SUCCESS(rv, rv); + ToLowerCase(tagName); + associatedTags.AppendElement(tagName); + queries[i]->SetTags(associatedTags); + } + options->SetResultType(nsINavHistoryQueryOptions::RESULTS_AS_URI); + historyService->QueriesToQueryString((nsINavHistoryQuery**)queries.Elements(), + queries.Length(), options, queryString); + + nsCOMPtr<mozIStorageBindingParams> replacementParams; + rv = replacementParamsArray->NewBindingParams(getter_AddRefs(replacementParams)); + NS_ENSURE_SUCCESS(rv, rv); + rv = replacementParams->BindInt64ByName(NS_LITERAL_CSTRING("id"), queryId); + NS_ENSURE_SUCCESS(rv, rv); + rv = replacementParams->BindUTF8StringByName(NS_LITERAL_CSTRING("new_url"), + queryString); + NS_ENSURE_SUCCESS(rv, rv); + rv = replacementParamsArray->AddParams(replacementParams); + NS_ENSURE_SUCCESS(rv, rv); + } + + rv = replacementStmt->BindParameters(replacementParamsArray); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr<mozIStoragePendingStatement> pendingStmt; + rv = replacementStmt->ExecuteAsync(nullptr, getter_AddRefs(pendingStmt)); + 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 @@ -14,17 +14,17 @@ #include "mozilla/storage/StatementCache.h" #include "mozilla/Attributes.h" #include "nsIEventTarget.h" #include "Shutdown.h" #include "nsCategoryCache.h" // This is the schema version. Update it at any schema change and add a // corresponding migrateVxx method below. -#define DATABASE_SCHEMA_VERSION 41 +#define DATABASE_SCHEMA_VERSION 42 // Fired after Places inited. #define TOPIC_PLACES_INIT_COMPLETE "places-init-complete" // 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. #define TOPIC_PROFILE_CHANGE_TEARDOWN "profile-change-teardown" // Fired when Places is shutting down. Any code should stop accessing Places @@ -286,16 +286,17 @@ protected: nsresult MigrateV34Up(); nsresult MigrateV35Up(); nsresult MigrateV36Up(); nsresult MigrateV37Up(); nsresult MigrateV38Up(); nsresult MigrateV39Up(); nsresult MigrateV40Up(); nsresult MigrateV41Up(); + nsresult MigrateV42Up(); nsresult UpdateBookmarkRootTitles(); friend class ConnectionShutdownBlocker; int64_t CreateMobileRoot(); nsresult GetItemsWithAnno(const nsACString& aAnnoName, int32_t aItemType, nsTArray<int64_t>& aItemIds);
--- 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 = 41; +const CURRENT_SCHEMA_VERSION = 42; const FIRST_UPGRADABLE_SCHEMA_VERSION = 30; 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 0000000000000000000000000000000000000000..8a8c03665d1495e392e4572e79c3da44698f2aed GIT binary patch literal 1146880 zc%1Fsdvsj)eHi$8VK4v*0^&oUC|QCm(-JKa1mB_(Spwe_pW;iT92Fg42EY)58E9re z5Rix~y4jq>m3!)*?$+_i!)}|pjvd>(NmHH1@n*fwwsxDX;yMp2uI<LLEazp%m8~4x zyWM+dFaSt`beq#XN~6!`fcHD!-~0aN-is^!gHw+mDb(^k!{y3&w$`&X_FOEHh&|fV z6N|-K!+-b07Tgv7OD)Rd;lB;Bn`@2ruKrIQmus;%kG&L|-GBMnXJ;;NedfK-9+>H! z-Z%Z#OJmc^FWolv$kgX9uBp9Ldw1>5>c6ZWu6(ibV^9Cl(~nJjdgATne_K9MdZF~t z_`eumRlE`o0ssI2000000000000000000000000000030zqs<2H@9{_et-PR`9dj| zzcgN+87S29<JEy|sZ_4kKWA&TN?~xamS3vHJ$p{>>p#7(=ghG~kDuAsbLiOKedl_9 z_$WQck6qiU=e{TzDC8dKiSmK@4q?%TE1h?@b|1Jaer4bMA{s+a6thG5>sMH>eDjUg zcEhgCWz8+9-r`0px~r$P`~3R&m80{Mj^(GP%9Y&Q$P?viEtjoj2PR8}rzi8*wYvL8 ztNY=@G?&?Ixp<{f`+=y#hAWA?TDuS38^3b*{M>`(^4NH`GFBZJnJnb4UD<qX&yALL z<NnPhEsSwr)MUf6ceZx#3)gVl5^D%ona}2O`P}t&cg?L}sj08-w#39ygZ`+&hAX$c zskQr5IQ{k|rjIVSVzycvFXsxwg{4+;=S^0zWbY*wGd%V{Pt@+{T)PdI?`Z9Q<Obi$ zM7EMI)vm92(@hq#sLvA9H)`vbMWI$Gmo_|odu#WPgwuE2U`MVmzT=1N!p+XSHa(Hw za3#5}wR_K|#iu-4sMgAr>5GMMRgG_S>8jhWoBIYmoAX~)-E&_()?0<Exb5bv7_OAZ zm#$j(Lsl{0^9HMk>Idq{hAZ7`Tf2{jt9axFtEk_}H{Ouje#la;?tX*yEIch1?a#&T zaC`2KUs*FhalP5nkBUas+8aG5ZqT#2oJRHB6Jqw(@X3Dn;;&_{cXZd(*LT%!G%CBV z+lCu<x^~j2#lWKT9$(Yi{ct#c+qLr#SMo#o($MrZ<+t8w>Z{scJ8QFY!-dtY-8+|> zuD+z9@?@#DP`Ktsb1gK!cB00a)vGq-SG9I;59f+sJJ)FVOX8Zc)i;`IzU{R$Rq_|> zl^e!a-e3nOE5(7)Y<2XS@>MsQ>Z<nFPS>p5aQK#V_xknmchs_j#eDVYV)zUisMfNz z&5J*;oSWlx|L!CEdKTB*R~sxoP{>_+Aj&qp^TDpx?)&eL|M<Q2;jVcnxhB2kszI-N zf9dHd<ie9Wz3;%jlRYO+9y;28@_f(XedizOnR~ODuRM1AbkDIfM~*D<V$p2#K+jOQ zR12ToJx7ioJ8-uD<er25Vc~EwJ5rq=XYaoK{b!Dx?&*7=Cx2<8P{~G*_@!#2=kQYH z^S>&Ws$BeQ>-zc)@4UCOwfnBS;y?bT`K_DZwEBmx>$dBMZdCKh^fzm#u5@*@c5mGp zzjENJomx1X>JmjOmfXEX4Q{rNrR;dVXKpL*+qz{#^AX+L&JFukq`S{=jmPS@+T87t zovfAX`M?sh4QyVbu=B+7baeNezv^DhhtK=c(6wpDl6T}><$ckD9|*GzSMF?Yjc)HN z!Teq|Uvn3KUUuE?EW8ZgXj^*v&zwGf=vdhE=)Pm8ALyCu(Q|s=xzlqSbo~{$=#9GG zaKp}J>F&epZ?<#uFYuceXUnhLo(UiQJL6Z*%%9n!m)d+WU(4r~ENr{(T(4^qopkiu zv~ez1T?)~KdorBuV*dKOYSA@%s4d<7)Xtlo)^(FM&hNV7_IHl7wszlpZ~TAixc)@1 zN-{S-v724oi*M2JJ|Da8j%z$H8n3=reUF93cYQcep;pXa{nVKIwywGWhRelVzA{js zHrn4M9tKN40v=fSP9Er~PM3yGg}-tpt2g-?8qM~MW=kXaJ<;#*eC5WiH*dJ|Kui4* z{LY2nUeRyl>imcFbvs-yzS-_Z4H}ox{DjSWqxllEXa~bRx!GkE?s_FZT)5P@bvCEd z-TUsk*)>*QUE{i{e{8+7BiY)$abx_QcP;#WE=^3<=H6(p%~RL^hF(+~o%T(B!_=ED zz8n_SO;+=bm#T$xU;JcQ^h#YHWy6L?Qt9qf8*g^5i&xe-<;5i}u~;lq|35Sb60O~v zHpSoh@WOYz_;voeg5>qz`PHxgH~#Jy{zGNaZFAiNapC3p+On(f%xb<`U39-~_&Yn} zt=)Iu9sh}4^Jmg{xm);@xNhyu*T0?{uR44)y!s|y;=XRatX^HZ=w*4SU+bfV@E%{{ zEn(4harF(bREukGn)yZ>H!o^+&3~25H@@l-GF-0Y3nQf)EqL+eI`^`ClSbjtxqNk~ zQkbYe6dN;yck9JMerljFo*l_Y@8gXdw<Ej`yPoNe1^+nMGy5mA@0neF`74(zm)oBG z%(GXXJ@o9dXa2)8?|bISumJ!700000000000000000000000000000000000Z)E*l zov}l)Sf(wLiLGx-$71zAR{qd%xtPmW9zJty|DhxM_73bme*Ey!{*#AK)wb@h4DX(~ zG<NAse=PRuwlDq4AHVkY&d%7L8xAqwb>Qf}V`m1Q+_G`!;oAAB?07Ef-*@jFZ`-+| zGq(MP{ZAi1eq?w5$^NYyhYxH#@z{<F`SGaZFCP0_+jn$y#<pJHG5UMv5AS$uwOpw^ zym{M9zIyC%b^5`JM>j_OHhkkf$9{BqXY7$B`c<-}Y9abd;t%g^^xig5JGJ*@fBzGs zM;<#G_5R5H8$Y&V*^Ou2I@fRe6T>I_vNN><$0v3~{enOG(3khOcg7x%b>4inZynEG zI+?Fd7HidqH~01K8$3H%I$GY_w`*U0yUslJI}g0Qtuxkt{W$dj9=xW1b=UBr!TxQd z6PFH@qW(Mn;6wjtBolsJ8@9G(+7jvYZ7s3bXstF;eQ49BaGX-U);nIFDHMy@-g0GR z(}{Xf&(TKFNTuAHEfxzi<?-HHzLYIa^k#04wVa=N`IAqL?mT{C?BJ>F+5Oes7w%tq z?DCqo6kEGuS6XBDWHOmV?5df@3xk7&TJKPKeA6S<eC1;JH5V#Fi)Vi8&}g<)$`>DA zzh!HwFfv*zPLG#|dTUX~@!r_;`N=J{qt)#LXHH(|zklT;=kIvimUL(A-tcQ_OcriS zcCb8IduXVVt&ZlavF9%qpFY2P-^I$*(C~#=?B{>smwG<+@n>4X6|RYGy?%w+T&`TI zUbPqb(#A8VHVqdl`Qh@VMuSarOWRn>S8Mso##*^ttZrJoP1RC4m&=9=-Zj)$*?n;5 zWci7)v-hujzVG0^bE)Xg+trvmzG&`T{$jpZp2%0Ontt)9mHb4x7`DoV1CLhw4<9@@ zbK=<k)49a`EC0zq9R29oWM}L^Z0GeO4^ECW2E5V0^*Ay)7>@h+h4I7NCWlUMJe=Jc zj{EsPzwp#kiO$&Z*dy2P)?l$b(mS^oHynGqSQ{NFlzK<X#c2E!C-z?~96P*g@6N%! z_pkiu53c;~kHtG<1F?<Q4?i_EwZu0uR4&yjg~7>M{;FxB#<AyfCyK>lrSHOH(=+Gq zUpf4__dnASKJ4PbpT)u-000000000000000000000000000000000000001ZW4I;W z8q37f@k}Pxa(?RNPd+u8=m?AB!FOZf4*&oF000000000000000000000000000000 z00000yuoDRsrah4%y@aGJ<Q_4Yq9VL00000000000000000000000000000000000 z0002qXfmnzs<zB{d8Q*wT7ySp!Jh=52|gS=A4~*47N!6I00000000000000000000 z0000000000000000Px>rU28hFInfrIC}vCfTJLyyrcf+qd&`xP%<U`F>Db+gOl-U` zI9RCl4wc7a&#!BV+Gk?fT&`TIE^Z%t{!OWLY-d=P%U{eF%M<y^)zx<-)3NPg_2A@4 zqx7oUo<utKa8z3?kMx!bBcrwA^wlj^#nZ8SqU9_uY!A<&HOR$+Zv_7?_~qcIgLehb zged?3000000000000000000000000000000000000Q`4%YjSgZqL?k^YrW&;nL@Fc z?JZYET300RX<L-9NZuVEFANSAYQ00{@$_xUo$*}$V!l|O$X6EkORY_Aj}K0cGzu0~ zB=1N*93L!}M|w+zk<nUldU0hUT0}ONE0?N^i(5Lv^{tz^B^G=x_+Nr2g7vdspZ(Bm zdG@WBzk2xtm(N{Z{p{zT{d>=zcy`S*fAGvNJu~~v!DqT=zBu#qGZQoYVJ83p00000 z00000000000000000000000000001hH-g@ED`IW&cs%@=POoo^cXcOXE$?{cT`zq& z)`*{d^wi0xPi#B2wR+^qzP-tfx7FLXHQRS~H<~?{X~Yk%o!ENd%(*9v<<pNHKa||K zw%$C`Y`$Wy`O!xk@zR4&A1<FCd}8mBslmfLlRwhEA{LLu<M+-rPpnGz7mGc+%jL20 zY-Oz4=(OvF<`PTWvweGZOr0Aoof}SXxwUaBYvww4tV*2Dj?DE*f2$F<zgVkG6%USH z*gEj|fn0LOn#SqeHP>hPs=ei@Qn8%PHTr$w_q!VLzw}%<{`lFmyCydG5A1(Dxovgh zyl$WCw`|qHLbX<|OgCEp!of!T{C#~-OxK2w>^M0#HvU9%<ErTN+U7Sq(b8x(bE0`- z$HsO{4W2rXJv=n9qxFH6_1$Rhbz3t0YR*r++>+{R#zQmH2m8y#>~M8(<iT4SOS)%n z@b<*X{7}AB>uGF4*dhLbSUsjb^WKBUMo(0>KY8Z#&i&=YuC7MU^>aPjl1IzcTF==+ zwNT6FqAp*1?dP6r4DqvTbK4)A-qUxncI41VU*bnQ8+*Q?-Zc}?WYQ<<fBL-VXydPr z*L#2G)~-f;(^vNOPnDl|eE9HV1Lbpxy(=2Mcg^)~&9v+-REH+3)t+OGziHl6F7+Hb zxu?;){ahoic=S>^xA}PP-1M1a2ev2P-q9Fk<J>6eOe(r)rpuF+o}o&%I-0Ma{?}gp z-V2TXiJ#1GJ2&1}-ZfZ0I&vVfdwHY(_PPEonPhYJ)A?#GU+FnrE*Iy<_-3pTzx(2z zTHm4ZEgO#vT*y9|IIyfS#v^lMq%w)Q4d~h1{HyLg$MUtQa%HSBz?c7aBYwT_*r6>) z_wDH4dgkKHzT~6rjSF(?+(5}pbDZ6iBR!`p*`cv~rP1?Wf2$FH@9%Hjv-jxqz}bz* z&+gxv>~Cv)o6XZnWcC*-`Qh@V`8|#PXndpVCmw9XPiGF!?9X55JDwY=O+KACkZIh8 z&9g})64#D#GG7h%cc{6wAAhd-MW1-OR4QycvAH<CYj5Vk)^IK11+~6;HV48h<51~h zxp;Bz!g}>*{<0B2^4_D{P7ZGwcyROfv7<wYA5AxIm5p=Pej;_WJXk2^7e5QaZeLvW zLNkun>j&z7w58Ge{^pIAJYE|OzkzEycfQz|`$FGjX*leht9MTHr5e50H0Mq<Z=v}v zVXTE^19Kiu)_b&VYR)%5QT^H(%vP@I9o|i~MsdA&%R`BJ_soXtyC2P$CKvaO?)vet ztT}Vb1M#SDCezXEyU@DvXsMLTwPx!?NA%2p?)Ce10{{R300000000000000000000 z000000000000000e;u?(hv#1BMF0Q*00000000000000000000000000000000000 z!0W6d{Fey+Bo=%(JOBUy00000000000000000000000000000000000z~6jriI&)^ zcx*gZN@e0Lv0LJ?!I@-xSdj?69Sgn}9smFU000000000000000000000000000000 z00000;0>cSxhg)MD<xWzx3mq;bc7}G;Ll^>4*&oF0000000000000000000000000 z0000000000yg_uulk4N-<(Yx8{Pa}0lB*^<!qRkbAQpT*_;m2$;O_+&!UO;S00000 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 z)4{I?zZ!fj_~&5*000000000000000000000000000000000000001ZJ*AV0c;@y@ z6k^Y}HbS})S{fnM2+2lBv?LR4VPP^8?}`qc(P2e&=!g!>qr<Z3&>kJy+QTWjf)B-l z9|W%j-wysPcqRC1@TK4j!RLd2AAC0W&EPkJUkiRUYybcN00000000000000000000 z0000000000000000Dm1UPp0Fsmh)3DfAXo(Oez_RcSVQJ=&&L>bVP^c(P3G1XpatU z(V;auq@zPibVx;qWOPU*ld-n;aOketzla6j489cnR`7}7!@>K4p9(GoBf;5Vf3Q7x zORy?Pgbe@y000000000000000000000000000000000000Qj4wE4e;?vH0})-TN+9 zriO+uq@#k`*8P>?-7}ZQE}iM`iVCW`h7S$)ZyTMsbfDB06^vH<4<9@@bK=<k)45bs zke}RAJ6hd7aOUKN{?^Xq`nKGOVzF51yYSfb%=xInK<(7tll}cqj2?OH=<<5OlUp|K zJX||Jl^xG@Mg@;w7(cvia_IEN!`ZFLsNlqj{TB<z4)5B#b8v4WD%dx8cCvJ|yti-H zzFd1$(7$!#@PUmd9@}vtKfWR=*frEw*?n;5Wci7)vn^4<&f_P>4xY-M-Cx~(ArlpB ze`5G#Uv{Q;;P}LjWl_PlnSAxw;p+5*7msf2Xb-m}9(+3%{r~^~000000000000000 z00000000000000000000z#B+MDxPl3#9Gczz5L0iM%%-Zc<_T*_yYg{0000000000 z00000000000000000000000000B=aiWTve>jPc;rSoi|~00000000000000000000 z000000000000000003_&%aZA~Sj+jTmp}Q`XnUBi3y#Es?*;!k_|xEP!Iy&H3tkL9 z6MQQ8wcuX_9}PYjyg&Hq;2#8kH~8`3a&R#y2ctnYcrrK@b^-tZ000000000000000 z00000000000000000000fF-0;@wRx^x_aomtsYjat%r_V>tXqtdRVr)9@=lIhqkVI z$aL02>xz0vchp16@_I-utA}KJJtW#%Qf*<&bSf5)Cs)=(qPr1RHA1El(v8s42&qO$ zHbSB$3~h<_aNX;IeX-#C!FPgh2CoGFDR?RP55d0+elz&>;8%l>1^+zwK=9MSdxD<| zt_0ITB^V2af_DU`gQH<50000000000000000000000000000000000000013tTh#n z$Ga2j(y4e`y!*B=Wa8ax>!qub-Hn*IwO+ewO}%#2>UwD=-PwpOD;hD?(TK_AjhI+g z@0Dq<_sX=@vt(;MOLo=UBodi=X<}u)G;vEh)fSJptg44px*n1(^^i!lq}sy%;W2EO zN@X%_?cws*&Hh9z_;&C|!S4qDCiqvuM}nUV-V^+tU@{m9&IN~phl2ZqJA#hc|9AGA zvtOD0?b%Pw{_^ZUo&BG~P5=M^00000000000000000000000000000000000{CBn@ z6;H>zVlD4@<y|j*_R)3qw6l?ZaP4jNbVVb*^x)ch+R;e2zj$jsU0zSW@Lzh?)YD~+ z^z-+vuBYv>mh)3Dr#|!ETk2_BJ&phD+OB$<iPEoq=S^Sftf#Gwbj719>S?->CVsM` zp0?D}SHJt>@_L$Tq+jn_R!@_S^!NULdp%7w(objFI#O-vcq9ABds{nFu_%j9;KQ-* zMcJ+wRxJ;+w#M>jPRvz>t8Gd3t-LBr%x7PF?dP6LE@a=iwJWoby?UXqWg+|Gsu$9W zvd$M%?cwgPoBdcUcs2NX@KW$w!EXe=9Q?E3r-SE%AgBcS;9PJxczdupcuUY7w9o!O zv)`Ki>g?~%erEO)VJ83p000000000000000000000000000000000000A4T4lhNO< zf5$8Ddg;TlbqmRJncEhUqmQm#NOrx@y^y58b?bcch2QU5vylA4!PN`N%!yTv<owjj zEvdeh^GW;zv0IwS*IxU%=ep*T@7&texsbG<TQQ%!`n?xA7LsqqmM<h<{@cqIl7IcJ z_J!mV54J5NAAc^hko?SFw$3MCT=hbFA?bXvWpR>PoFo?~iDk)jTQg}7H+9|Yfmra( z;7h@81)m5$9K0|1so+vD5}Xb81>1sqf|Wsh_FJ=Gp8daPe|`2Nvp+NYcV}m23$y3K zP5=M^00000000000000000000000000000000000T&pX&K7Og3+k8BCZu-oz1KU?b z1(T)WLNT8kh_plnwaQfS;OK>|1CJlb-C8dwmy3hhO1(w0UXUHB4$Mh)CfB!B%H>+K zAZqaR;qv*xC-xqh8a%vnZB#I^^}v~PPZrCkA3J{Nwy2=AJ=?cu$JDve(z)U8s9?N2 z7%rnRM7my(FHJVbOhg42jz50(?5>H;{R8_SUlSGNx1AgBEAJXCA00W+5f$XNKQ_Im z?_%x9p^?6>sNmGer%!A<wY7TW$-ceoqJk4omr8|gCpH(SckRtY1;@sAObwnokUcy! zuwzxd;Lw(%`*!qiJ#%qp-?FIS=(dx?TLvE7ynXEGP-|3hXlD9gf4P_)t`3f@j0z6U z?9X55JDwY=O+MXLFE}=OqO$$TGpBd%FW(Xs?CYN@Kk@kR;l~Eb=Q^W;J+;0=<6AZ! z8Mu&ra(Ps+b<f_T(*tKW9zVN(YkO4C_r!E<_{ffvV`Jk_tZom#yhQMyW5M^r0{{R3 z00000000000000000000000000000000000ym6#c@mM^SXbD4GvOOw!?sZ-f00000 z0000000000000000000000000000000002I&f3F&tAoK<@Rz~2gFg=bFnB5WT=3c8 zQ^CIq{<q*4gMS>nFZc(+yMlKHGeIS|5H<t=000000000000000000000000000000 z000000D!-e)>J$mUzc9hh^f`}cw73GMog`&$F=FsMoe|pV|RK*Bc?j)@z(URMocZQ u$2IA;MohKW<LY#4Bc?L-xT+=Hh^dx(yd{~c$F5|uB^7ULYt$waiT@9h2iybz
new file mode 100644 --- /dev/null +++ b/toolkit/components/places/tests/migration/test_current_from_v41.js @@ -0,0 +1,187 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const { TYPE_BOOKMARK, TYPE_FOLDER } = Ci.nsINavBookmarksService; + +// Hashes are precalculated since URL generated each time is a constant. +// They will need to be recalculated in case the test is changed. +const gQueryData = [ + { + statement: `INSERT INTO moz_places (url, guid, url_hash) + SELECT 'place:folder='||b.id||'&type=7&queryType=1', 'query1______', 268507487492160 + FROM moz_bookmarks b + WHERE b.guid = 'tag1________';`, + guid: "query1______", + oldQueryString: null, + newQueryString: null + }, + { + statement: `INSERT INTO moz_places (url, guid, url_hash) + SELECT 'place:folder=' + ||(SELECT id FROM moz_bookmarks WHERE guid = 'tag1________')|| + '&type=7&queryType=1&OR&folder=' + ||(SELECT id FROM moz_bookmarks WHERE guid = 'tag2________')|| + '&type=7&queryType=1', 'query2______', 268505990850137`, + guid: "query2______", + oldQueryString: null, + newQueryString: null + } +]; + +function makeTagGuid(tagName) { + // Be careful about having |tagName| > 12, since the guids + // will be unique only if tagName[0:12] is unique. + if (tagName.length >= 12) { + return tagName.slice(0, 12); + } + + return tagName + "_".repeat(12 - tagName.length); +} + +async function getIdForTag(tagName) { + return PlacesUtils.promiseItemId(makeTagGuid(tagName)); +} + +function copyPropertiesExcept(obj, except) { + let newObj = {}; + for (const property in obj) { + if (obj.hasOwnProperty(property) && + !except.includes(property) && + typeof(obj[property]) !== "function") { + newObj[property] = obj[property]; + } + } + return newObj; +} + +async function insertPlacesQuery(db, qData) { + // Note that we cannot use Places.history here. + await db.execute(qData.statement); + const rows = await db.execute(`SELECT url FROM moz_places WHERE guid=:guid`, + { guid: qData.guid }); + Assert.equal(rows.length, 1); + qData.oldQueryString = rows[0].getResultByName("url"); +} + +async function validateMigration(qData) { + qData.newQueryString = (await PlacesUtils.history.fetch(qData.guid)).url; + do_print(`Old query string: ${qData.oldQueryString}`); + do_print(`New query string: ${qData.newQueryString}`); + let oldQueries = {}, oldOptions = {}; + PlacesUtils.history.queryStringToQueries(qData.oldQueryString, + oldQueries, + { }, + oldOptions); + let newQueries = {}, newOptions = {}; + PlacesUtils.history.queryStringToQueries(qData.newQueryString, + newQueries, + { }, + newOptions); + + // Check options. + // Query types should be different. + Assert.equal(oldOptions.value.resultType, 7, "Old result type is 7."); + Assert.equal(newOptions.value.resultType, 0, "New result type is 0."); + // Rest of the options object should be the same. + Assert.deepEqual(copyPropertiesExcept(oldOptions.value, ["resultType"]), + copyPropertiesExcept(newOptions.value, ["resultType"]), + "Options are identical except for resultType."); + + // Check queries. + Assert.equal(oldQueries.value.length, newQueries.value.length, "Number of queries is same."); + for (let i = 0; i < oldQueries.value.length; i++) { + // Number of tags == folderCount == 1. + // Folder ids should be replaced by correct tags. + Assert.equal(newQueries.value[i].tags.length, 1); + Assert.equal(oldQueries.value[i].getFolders().length, 1); + const tagName = newQueries.value[i].tags[0]; + const folderId = oldQueries.value[i].getFolders()[0]; + Assert.equal(folderId, (await getIdForTag(tagName))); + + // Rest of the query object should be the same. + Assert.deepEqual(copyPropertiesExcept(oldQueries.value[i], ["folderCount", "tags"]), + copyPropertiesExcept(newQueries.value[i], ["folderCount", "tags"]), + "Query/Queries is/are identical except for folderCount and tags."); + } + + + // Query result URIs should be identical. + const oldRoot = PlacesUtils.history.executeQueries(oldQueries.value, + oldQueries.value.length, + oldOptions.value).root; + const newRoot = PlacesUtils.history.executeQueries(newQueries.value, + newQueries.value.length, + newOptions.value).root; + + oldRoot.containerOpen = true; + newRoot.containerOpen = true; + const oldUris = []; + const newUris = []; + Assert.equal(oldRoot.childCount, newRoot.childCount, "Number of results is the same."); + for (let i = 0; i < oldRoot.childCount; i++) { + oldUris.push(oldRoot.getChild(i).uri); + newUris.push(newRoot.getChild(i).uri); + } + + for (const uri of oldUris) { + Assert.ok(newUris.includes(uri)); + } +} + +add_task(async function setup() { + await setupPlacesDatabase("places_v41.sqlite"); + let path = OS.Path.join(OS.Constants.Path.profileDir, DB_FILENAME); + let db = await Sqlite.openConnection({ path }); + // Add pages. + await db.execute(`INSERT INTO moz_places (url, guid, rev_host, hidden, frecency, url_hash) + VALUES ("http://test1.com/", "test1_______", "moc.1tset.", 0, -1, 125509083372812) + , ("http://test2.com/", "test2_______", "moc.2tset.", 0, -1, 125508024173255) + , ("http://test3.com/", "test3_______", "moc.3tset.", 0, -1, 125511593739455) + `); + // Add bookmarks. + let now = Date.now() * 1000; + let index = 0; + await db.execute(`INSERT INTO moz_bookmarks (type, fk, parent, position, dateAdded, lastModified, guid) + VALUES (${TYPE_BOOKMARK}, (SELECT id FROM moz_places WHERE guid = 'test1_______'), (SELECT id FROM moz_places WHERE guid = 'toolbar_____'), ${index++}, ${now}, ${now}, "bookmark1___") + , (${TYPE_BOOKMARK}, (SELECT id FROM moz_places WHERE guid = 'test2_______'), (SELECT id FROM moz_places WHERE guid = 'toolbar_____'), ${index++}, ${now}, ${now}, "bookmark2___") + , (${TYPE_BOOKMARK}, (SELECT id FROM moz_places WHERE guid = 'test3_______'), (SELECT id FROM moz_places WHERE guid = 'toolbar_____'), ${index++}, ${now}, ${now}, "bookmark3___") + `); + + // Add tag folders. + await db.execute(`INSERT INTO moz_bookmarks (type, parent, position, title, dateAdded, lastModified, guid) + VALUES (${TYPE_FOLDER}, (SELECT id FROM moz_bookmarks WHERE guid = 'tags________'), ${index++}, "tag1", ${now}, ${now}, "${makeTagGuid("tag1")}") + , (${TYPE_FOLDER}, (SELECT id FROM moz_bookmarks WHERE guid = 'tags________'), ${index++}, "tag2", ${now}, ${now}, "${makeTagGuid("tag2")}") + , (${TYPE_FOLDER}, (SELECT id FROM moz_bookmarks WHERE guid = 'tags________'), ${index++}, "tag3", ${now}, ${now}, "${makeTagGuid("tag3")}") + `); + + // Add tag-bookmark association + await db.execute(`INSERT INTO moz_bookmarks (type, fk, parent, position, dateAdded, lastModified, guid) + VALUES (${TYPE_BOOKMARK}, (SELECT id FROM moz_places WHERE guid = 'test1_______'), (SELECT id FROM moz_bookmarks WHERE guid = 'tag1________'),${index++}, ${now}, ${now}, "bmkTagAsc1__") + , (${TYPE_BOOKMARK}, (SELECT id FROM moz_places WHERE guid = 'test1_______'), (SELECT id FROM moz_bookmarks WHERE guid = 'tag2________'),${index++}, ${now}, ${now}, "bmkTagAsc2__") + , (${TYPE_BOOKMARK}, (SELECT id FROM moz_places WHERE guid = 'test1_______'), (SELECT id FROM moz_bookmarks WHERE guid = 'tag3________'),${index++}, ${now}, ${now}, "bmkTagAsc3__") + , (${TYPE_BOOKMARK}, (SELECT id FROM moz_places WHERE guid = 'test2_______'), (SELECT id FROM moz_bookmarks WHERE guid = 'tag1________'),${index++}, ${now}, ${now}, "bmkTagAsc4__") + , (${TYPE_BOOKMARK}, (SELECT id FROM moz_places WHERE guid = 'test2_______'), (SELECT id FROM moz_bookmarks WHERE guid = 'tag2________'),${index++}, ${now}, ${now}, "bmkTagAsc5__") + , (${TYPE_BOOKMARK}, (SELECT id FROM moz_places WHERE guid = 'test3_______'), (SELECT id FROM moz_bookmarks WHERE guid = 'tag1________'),${index++}, ${now}, ${now}, "bmkTagAsc6__") + `); + + // Add Places Queries. + for (const queryData of gQueryData) { + await insertPlacesQuery(db, queryData); + } + await db.close(); +}); + +add_task(async 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 = await PlacesUtils.promiseDBConnection(); + Assert.equal((await db.getSchemaVersion()), CURRENT_SCHEMA_VERSION); + + for (const queryData of gQueryData) { + await validateMigration(queryData); + } + + await db.close(); +});
--- a/toolkit/components/places/tests/migration/xpcshell.ini +++ b/toolkit/components/places/tests/migration/xpcshell.ini @@ -3,17 +3,19 @@ head = head_migration.js support-files = places_outdated.sqlite places_v31.sqlite places_v34.sqlite places_v35.sqlite places_v36.sqlite places_v38.sqlite + places_v41.sqlite [test_current_from_downgraded.js] [test_current_from_outdated.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_v41.js]