DBSchema.cpp 102 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "mozilla/dom/cache/DBSchema.h"
  6. #include "ipc/IPCMessageUtils.h"
  7. #include "mozilla/BasePrincipal.h"
  8. #include "mozilla/dom/HeadersBinding.h"
  9. #include "mozilla/dom/InternalHeaders.h"
  10. #include "mozilla/dom/RequestBinding.h"
  11. #include "mozilla/dom/ResponseBinding.h"
  12. #include "mozilla/dom/cache/CacheTypes.h"
  13. #include "mozilla/dom/cache/SavedTypes.h"
  14. #include "mozilla/dom/cache/Types.h"
  15. #include "mozilla/dom/cache/TypeUtils.h"
  16. #include "mozIStorageConnection.h"
  17. #include "mozIStorageStatement.h"
  18. #include "mozStorageHelper.h"
  19. #include "nsCOMPtr.h"
  20. #include "nsCRT.h"
  21. #include "nsHttp.h"
  22. #include "nsIContentPolicy.h"
  23. #include "nsICryptoHash.h"
  24. #include "nsNetCID.h"
  25. #include "nsPrintfCString.h"
  26. #include "nsTArray.h"
  27. namespace mozilla {
  28. namespace dom {
  29. namespace cache {
  30. namespace db {
  31. const int32_t kFirstShippedSchemaVersion = 15;
  32. namespace {
  33. // Update this whenever the DB schema is changed.
  34. const int32_t kLatestSchemaVersion = 24;
  35. // ---------
  36. // The following constants define the SQL schema. These are defined in the
  37. // same order the SQL should be executed in CreateOrMigrateSchema(). They are
  38. // broken out as constants for convenient use in validation and migration.
  39. // ---------
  40. // The caches table is the single source of truth about what Cache
  41. // objects exist for the origin. The contents of the Cache are stored
  42. // in the entries table that references back to caches.
  43. //
  44. // The caches table is also referenced from storage. Rows in storage
  45. // represent named Cache objects. There are cases, however, where
  46. // a Cache can still exist, but not be in a named Storage. For example,
  47. // when content is still using the Cache after CacheStorage::Delete()
  48. // has been run.
  49. //
  50. // For now, the caches table mainly exists for data integrity with
  51. // foreign keys, but could be expanded to contain additional cache object
  52. // information.
  53. //
  54. // AUTOINCREMENT is necessary to prevent CacheId values from being reused.
  55. const char* const kTableCaches =
  56. "CREATE TABLE caches ("
  57. "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT "
  58. ")";
  59. // Security blobs are quite large and duplicated for every Response from
  60. // the same https origin. This table is used to de-duplicate this data.
  61. const char* const kTableSecurityInfo =
  62. "CREATE TABLE security_info ("
  63. "id INTEGER NOT NULL PRIMARY KEY, "
  64. "hash BLOB NOT NULL, " // first 8-bytes of the sha1 hash of data column
  65. "data BLOB NOT NULL, " // full security info data, usually a few KB
  66. "refcount INTEGER NOT NULL"
  67. ")";
  68. // Index the smaller hash value instead of the large security data blob.
  69. const char* const kIndexSecurityInfoHash =
  70. "CREATE INDEX security_info_hash_index ON security_info (hash)";
  71. const char* const kTableEntries =
  72. "CREATE TABLE entries ("
  73. "id INTEGER NOT NULL PRIMARY KEY, "
  74. "request_method TEXT NOT NULL, "
  75. "request_url_no_query TEXT NOT NULL, "
  76. "request_url_no_query_hash BLOB NOT NULL, " // first 8-bytes of sha1 hash
  77. "request_url_query TEXT NOT NULL, "
  78. "request_url_query_hash BLOB NOT NULL, " // first 8-bytes of sha1 hash
  79. "request_referrer TEXT NOT NULL, "
  80. "request_headers_guard INTEGER NOT NULL, "
  81. "request_mode INTEGER NOT NULL, "
  82. "request_credentials INTEGER NOT NULL, "
  83. "request_contentpolicytype INTEGER NOT NULL, "
  84. "request_cache INTEGER NOT NULL, "
  85. "request_body_id TEXT NULL, "
  86. "response_type INTEGER NOT NULL, "
  87. "response_status INTEGER NOT NULL, "
  88. "response_status_text TEXT NOT NULL, "
  89. "response_headers_guard INTEGER NOT NULL, "
  90. "response_body_id TEXT NULL, "
  91. "response_security_info_id INTEGER NULL REFERENCES security_info(id), "
  92. "response_principal_info TEXT NOT NULL, "
  93. "cache_id INTEGER NOT NULL REFERENCES caches(id) ON DELETE CASCADE, "
  94. "request_redirect INTEGER NOT NULL, "
  95. "request_referrer_policy INTEGER NOT NULL, "
  96. "request_integrity TEXT NOT NULL, "
  97. "request_url_fragment TEXT NOT NULL"
  98. // New columns must be added at the end of table to migrate and
  99. // validate properly.
  100. ")";
  101. // Create an index to support the QueryCache() matching algorithm. This
  102. // needs to quickly find entries in a given Cache that match the request
  103. // URL. The url query is separated in order to support the ignoreSearch
  104. // option. Finally, we index hashes of the URL values instead of the
  105. // actual strings to avoid excessive disk bloat. The index will duplicate
  106. // the contents of the columsn in the index. The hash index will prune
  107. // the vast majority of values from the query result so that normal
  108. // scanning only has to be done on a few values to find an exact URL match.
  109. const char* const kIndexEntriesRequest =
  110. "CREATE INDEX entries_request_match_index "
  111. "ON entries (cache_id, request_url_no_query_hash, "
  112. "request_url_query_hash)";
  113. const char* const kTableRequestHeaders =
  114. "CREATE TABLE request_headers ("
  115. "name TEXT NOT NULL, "
  116. "value TEXT NOT NULL, "
  117. "entry_id INTEGER NOT NULL REFERENCES entries(id) ON DELETE CASCADE"
  118. ")";
  119. const char* const kTableResponseHeaders =
  120. "CREATE TABLE response_headers ("
  121. "name TEXT NOT NULL, "
  122. "value TEXT NOT NULL, "
  123. "entry_id INTEGER NOT NULL REFERENCES entries(id) ON DELETE CASCADE"
  124. ")";
  125. // We need an index on response_headers, but not on request_headers,
  126. // because we quickly need to determine if a VARY header is present.
  127. const char* const kIndexResponseHeadersName =
  128. "CREATE INDEX response_headers_name_index "
  129. "ON response_headers (name)";
  130. const char* const kTableResponseUrlList =
  131. "CREATE TABLE response_url_list ("
  132. "url TEXT NOT NULL, "
  133. "entry_id INTEGER NOT NULL REFERENCES entries(id) ON DELETE CASCADE"
  134. ")";
  135. // NOTE: key allows NULL below since that is how "" is represented
  136. // in a BLOB column. We use BLOB to avoid encoding issues
  137. // with storing DOMStrings.
  138. const char* const kTableStorage =
  139. "CREATE TABLE storage ("
  140. "namespace INTEGER NOT NULL, "
  141. "key BLOB NULL, "
  142. "cache_id INTEGER NOT NULL REFERENCES caches(id), "
  143. "PRIMARY KEY(namespace, key) "
  144. ")";
  145. // ---------
  146. // End schema definition
  147. // ---------
  148. const int32_t kMaxEntriesPerStatement = 255;
  149. const uint32_t kPageSize = 4 * 1024;
  150. // Grow the database in chunks to reduce fragmentation
  151. const uint32_t kGrowthSize = 32 * 1024;
  152. const uint32_t kGrowthPages = kGrowthSize / kPageSize;
  153. static_assert(kGrowthSize % kPageSize == 0,
  154. "Growth size must be multiple of page size");
  155. // Only release free pages when we have more than this limit
  156. const int32_t kMaxFreePages = kGrowthPages;
  157. // Limit WAL journal to a reasonable size
  158. const uint32_t kWalAutoCheckpointSize = 512 * 1024;
  159. const uint32_t kWalAutoCheckpointPages = kWalAutoCheckpointSize / kPageSize;
  160. static_assert(kWalAutoCheckpointSize % kPageSize == 0,
  161. "WAL checkpoint size must be multiple of page size");
  162. } // namespace
  163. // If any of the static_asserts below fail, it means that you have changed
  164. // the corresponding WebIDL enum in a way that may be incompatible with the
  165. // existing data stored in the DOM Cache. You would need to update the Cache
  166. // database schema accordingly and adjust the failing static_assert.
  167. static_assert(int(HeadersGuardEnum::None) == 0 &&
  168. int(HeadersGuardEnum::Request) == 1 &&
  169. int(HeadersGuardEnum::Request_no_cors) == 2 &&
  170. int(HeadersGuardEnum::Response) == 3 &&
  171. int(HeadersGuardEnum::Immutable) == 4 &&
  172. int(HeadersGuardEnum::EndGuard_) == 5,
  173. "HeadersGuardEnum values are as expected");
  174. static_assert(int(ReferrerPolicy::_empty) == 0 &&
  175. int(ReferrerPolicy::No_referrer) == 1 &&
  176. int(ReferrerPolicy::No_referrer_when_downgrade) == 2 &&
  177. int(ReferrerPolicy::Origin) == 3 &&
  178. int(ReferrerPolicy::Origin_when_cross_origin) == 4 &&
  179. int(ReferrerPolicy::Unsafe_url) == 5 &&
  180. int(ReferrerPolicy::EndGuard_) == 6,
  181. "ReferrerPolicy values are as expected");
  182. static_assert(int(RequestMode::Same_origin) == 0 &&
  183. int(RequestMode::No_cors) == 1 &&
  184. int(RequestMode::Cors) == 2 &&
  185. int(RequestMode::Navigate) == 3 &&
  186. int(RequestMode::EndGuard_) == 4,
  187. "RequestMode values are as expected");
  188. static_assert(int(RequestCredentials::Omit) == 0 &&
  189. int(RequestCredentials::Same_origin) == 1 &&
  190. int(RequestCredentials::Include) == 2 &&
  191. int(RequestCredentials::EndGuard_) == 3,
  192. "RequestCredentials values are as expected");
  193. static_assert(int(RequestCache::Default) == 0 &&
  194. int(RequestCache::No_store) == 1 &&
  195. int(RequestCache::Reload) == 2 &&
  196. int(RequestCache::No_cache) == 3 &&
  197. int(RequestCache::Force_cache) == 4 &&
  198. int(RequestCache::Only_if_cached) == 5 &&
  199. int(RequestCache::EndGuard_) == 6,
  200. "RequestCache values are as expected");
  201. static_assert(int(RequestRedirect::Follow) == 0 &&
  202. int(RequestRedirect::Error) == 1 &&
  203. int(RequestRedirect::Manual) == 2 &&
  204. int(RequestRedirect::EndGuard_) == 3,
  205. "RequestRedirect values are as expected");
  206. static_assert(int(ResponseType::Basic) == 0 &&
  207. int(ResponseType::Cors) == 1 &&
  208. int(ResponseType::Default) == 2 &&
  209. int(ResponseType::Error) == 3 &&
  210. int(ResponseType::Opaque) == 4 &&
  211. int(ResponseType::Opaqueredirect) == 5 &&
  212. int(ResponseType::EndGuard_) == 6,
  213. "ResponseType values are as expected");
  214. // If the static_asserts below fails, it means that you have changed the
  215. // Namespace enum in a way that may be incompatible with the existing data
  216. // stored in the DOM Cache. You would need to update the Cache database schema
  217. // accordingly and adjust the failing static_assert.
  218. static_assert(DEFAULT_NAMESPACE == 0 &&
  219. CHROME_ONLY_NAMESPACE == 1 &&
  220. NUMBER_OF_NAMESPACES == 2,
  221. "Namespace values are as expected");
  222. // If the static_asserts below fails, it means that you have changed the
  223. // nsContentPolicy enum in a way that may be incompatible with the existing data
  224. // stored in the DOM Cache. You would need to update the Cache database schema
  225. // accordingly and adjust the failing static_assert.
  226. static_assert(nsIContentPolicy::TYPE_INVALID == 0 &&
  227. nsIContentPolicy::TYPE_OTHER == 1 &&
  228. nsIContentPolicy::TYPE_SCRIPT == 2 &&
  229. nsIContentPolicy::TYPE_IMAGE == 3 &&
  230. nsIContentPolicy::TYPE_STYLESHEET == 4 &&
  231. nsIContentPolicy::TYPE_OBJECT == 5 &&
  232. nsIContentPolicy::TYPE_DOCUMENT == 6 &&
  233. nsIContentPolicy::TYPE_SUBDOCUMENT == 7 &&
  234. nsIContentPolicy::TYPE_REFRESH == 8 &&
  235. nsIContentPolicy::TYPE_XBL == 9 &&
  236. nsIContentPolicy::TYPE_PING == 10 &&
  237. nsIContentPolicy::TYPE_XMLHTTPREQUEST == 11 &&
  238. nsIContentPolicy::TYPE_DATAREQUEST == 11 &&
  239. nsIContentPolicy::TYPE_OBJECT_SUBREQUEST == 12 &&
  240. nsIContentPolicy::TYPE_DTD == 13 &&
  241. nsIContentPolicy::TYPE_FONT == 14 &&
  242. nsIContentPolicy::TYPE_MEDIA == 15 &&
  243. nsIContentPolicy::TYPE_WEBSOCKET == 16 &&
  244. nsIContentPolicy::TYPE_CSP_REPORT == 17 &&
  245. nsIContentPolicy::TYPE_XSLT == 18 &&
  246. nsIContentPolicy::TYPE_BEACON == 19 &&
  247. nsIContentPolicy::TYPE_FETCH == 20 &&
  248. nsIContentPolicy::TYPE_IMAGESET == 21 &&
  249. nsIContentPolicy::TYPE_WEB_MANIFEST == 22 &&
  250. nsIContentPolicy::TYPE_INTERNAL_SCRIPT == 23 &&
  251. nsIContentPolicy::TYPE_INTERNAL_WORKER == 24 &&
  252. nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER == 25 &&
  253. nsIContentPolicy::TYPE_INTERNAL_EMBED == 26 &&
  254. nsIContentPolicy::TYPE_INTERNAL_OBJECT == 27 &&
  255. nsIContentPolicy::TYPE_INTERNAL_FRAME == 28 &&
  256. nsIContentPolicy::TYPE_INTERNAL_IFRAME == 29 &&
  257. nsIContentPolicy::TYPE_INTERNAL_AUDIO == 30 &&
  258. nsIContentPolicy::TYPE_INTERNAL_VIDEO == 31 &&
  259. nsIContentPolicy::TYPE_INTERNAL_TRACK == 32 &&
  260. nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST == 33 &&
  261. nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE == 34 &&
  262. nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER == 35 &&
  263. nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD == 36 &&
  264. nsIContentPolicy::TYPE_INTERNAL_IMAGE == 37 &&
  265. nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD == 38 &&
  266. nsIContentPolicy::TYPE_INTERNAL_STYLESHEET == 39 &&
  267. nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD == 40 &&
  268. nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON == 41 &&
  269. nsIContentPolicy::TYPE_SAVEAS_DOWNLOAD == 42,
  270. "nsContentPolicyType values are as expected");
  271. namespace {
  272. typedef int32_t EntryId;
  273. struct IdCount
  274. {
  275. IdCount() : mId(-1), mCount(0) { }
  276. explicit IdCount(int32_t aId) : mId(aId), mCount(1) { }
  277. int32_t mId;
  278. int32_t mCount;
  279. };
  280. static nsresult QueryAll(mozIStorageConnection* aConn, CacheId aCacheId,
  281. nsTArray<EntryId>& aEntryIdListOut);
  282. static nsresult QueryCache(mozIStorageConnection* aConn, CacheId aCacheId,
  283. const CacheRequest& aRequest,
  284. const CacheQueryParams& aParams,
  285. nsTArray<EntryId>& aEntryIdListOut,
  286. uint32_t aMaxResults = UINT32_MAX);
  287. static nsresult MatchByVaryHeader(mozIStorageConnection* aConn,
  288. const CacheRequest& aRequest,
  289. EntryId entryId, bool* aSuccessOut);
  290. static nsresult DeleteEntries(mozIStorageConnection* aConn,
  291. const nsTArray<EntryId>& aEntryIdList,
  292. nsTArray<nsID>& aDeletedBodyIdListOut,
  293. nsTArray<IdCount>& aDeletedSecurityIdListOut,
  294. uint32_t aPos=0, int32_t aLen=-1);
  295. static nsresult InsertSecurityInfo(mozIStorageConnection* aConn,
  296. nsICryptoHash* aCrypto,
  297. const nsACString& aData, int32_t *aIdOut);
  298. static nsresult DeleteSecurityInfo(mozIStorageConnection* aConn, int32_t aId,
  299. int32_t aCount);
  300. static nsresult DeleteSecurityInfoList(mozIStorageConnection* aConn,
  301. const nsTArray<IdCount>& aDeletedStorageIdList);
  302. static nsresult InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
  303. const CacheRequest& aRequest,
  304. const nsID* aRequestBodyId,
  305. const CacheResponse& aResponse,
  306. const nsID* aResponseBodyId);
  307. static nsresult ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
  308. SavedResponse* aSavedResponseOut);
  309. static nsresult ReadRequest(mozIStorageConnection* aConn, EntryId aEntryId,
  310. SavedRequest* aSavedRequestOut);
  311. static void AppendListParamsToQuery(nsACString& aQuery,
  312. const nsTArray<EntryId>& aEntryIdList,
  313. uint32_t aPos, int32_t aLen);
  314. static nsresult BindListParamsToQuery(mozIStorageStatement* aState,
  315. const nsTArray<EntryId>& aEntryIdList,
  316. uint32_t aPos, int32_t aLen);
  317. static nsresult BindId(mozIStorageStatement* aState, const nsACString& aName,
  318. const nsID* aId);
  319. static nsresult ExtractId(mozIStorageStatement* aState, uint32_t aPos,
  320. nsID* aIdOut);
  321. static nsresult CreateAndBindKeyStatement(mozIStorageConnection* aConn,
  322. const char* aQueryFormat,
  323. const nsAString& aKey,
  324. mozIStorageStatement** aStateOut);
  325. static nsresult HashCString(nsICryptoHash* aCrypto, const nsACString& aIn,
  326. nsACString& aOut);
  327. nsresult Validate(mozIStorageConnection* aConn);
  328. nsresult Migrate(mozIStorageConnection* aConn);
  329. } // namespace
  330. class MOZ_RAII AutoDisableForeignKeyChecking
  331. {
  332. public:
  333. explicit AutoDisableForeignKeyChecking(mozIStorageConnection* aConn)
  334. : mConn(aConn)
  335. , mForeignKeyCheckingDisabled(false)
  336. {
  337. nsCOMPtr<mozIStorageStatement> state;
  338. nsresult rv = mConn->CreateStatement(NS_LITERAL_CSTRING(
  339. "PRAGMA foreign_keys;"
  340. ), getter_AddRefs(state));
  341. if (NS_WARN_IF(NS_FAILED(rv))) { return; }
  342. bool hasMoreData = false;
  343. rv = state->ExecuteStep(&hasMoreData);
  344. if (NS_WARN_IF(NS_FAILED(rv))) { return; }
  345. int32_t mode;
  346. rv = state->GetInt32(0, &mode);
  347. if (NS_WARN_IF(NS_FAILED(rv))) { return; }
  348. if (mode) {
  349. nsresult rv = mConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  350. "PRAGMA foreign_keys = OFF;"
  351. ));
  352. if (NS_WARN_IF(NS_FAILED(rv))) { return; }
  353. mForeignKeyCheckingDisabled = true;
  354. }
  355. }
  356. ~AutoDisableForeignKeyChecking()
  357. {
  358. if (mForeignKeyCheckingDisabled) {
  359. nsresult rv = mConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  360. "PRAGMA foreign_keys = ON;"
  361. ));
  362. if (NS_WARN_IF(NS_FAILED(rv))) { return; }
  363. }
  364. }
  365. private:
  366. nsCOMPtr<mozIStorageConnection> mConn;
  367. bool mForeignKeyCheckingDisabled;
  368. };
  369. nsresult
  370. CreateOrMigrateSchema(mozIStorageConnection* aConn)
  371. {
  372. MOZ_ASSERT(!NS_IsMainThread());
  373. MOZ_DIAGNOSTIC_ASSERT(aConn);
  374. int32_t schemaVersion;
  375. nsresult rv = aConn->GetSchemaVersion(&schemaVersion);
  376. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  377. if (schemaVersion == kLatestSchemaVersion) {
  378. // We already have the correct schema version. Validate it matches
  379. // our expected schema and then proceed.
  380. rv = Validate(aConn);
  381. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  382. return rv;
  383. }
  384. // Turn off checking foreign keys before starting a transaction, and restore
  385. // it once we're done.
  386. AutoDisableForeignKeyChecking restoreForeignKeyChecking(aConn);
  387. mozStorageTransaction trans(aConn, false,
  388. mozIStorageConnection::TRANSACTION_IMMEDIATE);
  389. bool needVacuum = false;
  390. if (schemaVersion) {
  391. // A schema exists, but its not the current version. Attempt to
  392. // migrate it to our new schema.
  393. rv = Migrate(aConn);
  394. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  395. // Migrations happen infrequently and reflect a chance in DB structure.
  396. // This is a good time to rebuild the database. It also helps catch
  397. // if a new migration is incorrect by fast failing on the corruption.
  398. needVacuum = true;
  399. } else {
  400. // There is no schema installed. Create the database from scratch.
  401. rv = aConn->ExecuteSimpleSQL(nsDependentCString(kTableCaches));
  402. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  403. rv = aConn->ExecuteSimpleSQL(nsDependentCString(kTableSecurityInfo));
  404. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  405. rv = aConn->ExecuteSimpleSQL(nsDependentCString(kIndexSecurityInfoHash));
  406. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  407. rv = aConn->ExecuteSimpleSQL(nsDependentCString(kTableEntries));
  408. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  409. rv = aConn->ExecuteSimpleSQL(nsDependentCString(kIndexEntriesRequest));
  410. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  411. rv = aConn->ExecuteSimpleSQL(nsDependentCString(kTableRequestHeaders));
  412. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  413. rv = aConn->ExecuteSimpleSQL(nsDependentCString(kTableResponseHeaders));
  414. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  415. rv = aConn->ExecuteSimpleSQL(nsDependentCString(kIndexResponseHeadersName));
  416. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  417. rv = aConn->ExecuteSimpleSQL(nsDependentCString(kTableResponseUrlList));
  418. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  419. rv = aConn->ExecuteSimpleSQL(nsDependentCString(kTableStorage));
  420. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  421. rv = aConn->SetSchemaVersion(kLatestSchemaVersion);
  422. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  423. rv = aConn->GetSchemaVersion(&schemaVersion);
  424. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  425. }
  426. rv = Validate(aConn);
  427. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  428. rv = trans.Commit();
  429. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  430. if (needVacuum) {
  431. // Unfortunately, this must be performed outside of the transaction.
  432. aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("VACUUM"));
  433. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  434. }
  435. return rv;
  436. }
  437. nsresult
  438. InitializeConnection(mozIStorageConnection* aConn)
  439. {
  440. MOZ_ASSERT(!NS_IsMainThread());
  441. MOZ_DIAGNOSTIC_ASSERT(aConn);
  442. // This function needs to perform per-connection initialization tasks that
  443. // need to happen regardless of the schema.
  444. nsPrintfCString pragmas(
  445. // Use a smaller page size to improve perf/footprint; default is too large
  446. "PRAGMA page_size = %u; "
  447. // Enable auto_vacuum; this must happen after page_size and before WAL
  448. "PRAGMA auto_vacuum = INCREMENTAL; "
  449. "PRAGMA foreign_keys = ON; ",
  450. kPageSize
  451. );
  452. // Note, the default encoding of UTF-8 is preferred. mozStorage does all
  453. // the work necessary to convert UTF-16 nsString values for us. We don't
  454. // need ordering and the binary equality operations are correct. So, do
  455. // NOT set PRAGMA encoding to UTF-16.
  456. nsresult rv = aConn->ExecuteSimpleSQL(pragmas);
  457. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  458. // Limit fragmentation by growing the database by many pages at once.
  459. rv = aConn->SetGrowthIncrement(kGrowthSize, EmptyCString());
  460. if (rv == NS_ERROR_FILE_TOO_BIG) {
  461. NS_WARNING("Not enough disk space to set sqlite growth increment.");
  462. rv = NS_OK;
  463. }
  464. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  465. // Enable WAL journaling. This must be performed in a separate transaction
  466. // after changing the page_size and enabling auto_vacuum.
  467. nsPrintfCString wal(
  468. // WAL journal can grow to given number of *pages*
  469. "PRAGMA wal_autocheckpoint = %u; "
  470. // Always truncate the journal back to given number of *bytes*
  471. "PRAGMA journal_size_limit = %u; "
  472. // WAL must be enabled at the end to allow page size to be changed, etc.
  473. "PRAGMA journal_mode = WAL; ",
  474. kWalAutoCheckpointPages,
  475. kWalAutoCheckpointSize
  476. );
  477. rv = aConn->ExecuteSimpleSQL(wal);
  478. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  479. // Verify that we successfully set the vacuum mode to incremental. It
  480. // is very easy to put the database in a state where the auto_vacuum
  481. // pragma above fails silently.
  482. #ifdef DEBUG
  483. nsCOMPtr<mozIStorageStatement> state;
  484. rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  485. "PRAGMA auto_vacuum;"
  486. ), getter_AddRefs(state));
  487. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  488. bool hasMoreData = false;
  489. rv = state->ExecuteStep(&hasMoreData);
  490. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  491. int32_t mode;
  492. rv = state->GetInt32(0, &mode);
  493. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  494. // integer value 2 is incremental mode
  495. if (NS_WARN_IF(mode != 2)) { return NS_ERROR_UNEXPECTED; }
  496. #endif
  497. return NS_OK;
  498. }
  499. nsresult
  500. CreateCacheId(mozIStorageConnection* aConn, CacheId* aCacheIdOut)
  501. {
  502. MOZ_ASSERT(!NS_IsMainThread());
  503. MOZ_DIAGNOSTIC_ASSERT(aConn);
  504. MOZ_DIAGNOSTIC_ASSERT(aCacheIdOut);
  505. nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  506. "INSERT INTO caches DEFAULT VALUES;"
  507. ));
  508. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  509. nsCOMPtr<mozIStorageStatement> state;
  510. rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  511. "SELECT last_insert_rowid()"
  512. ), getter_AddRefs(state));
  513. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  514. bool hasMoreData = false;
  515. rv = state->ExecuteStep(&hasMoreData);
  516. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  517. if (NS_WARN_IF(!hasMoreData)) { return NS_ERROR_UNEXPECTED; }
  518. rv = state->GetInt64(0, aCacheIdOut);
  519. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  520. return rv;
  521. }
  522. nsresult
  523. DeleteCacheId(mozIStorageConnection* aConn, CacheId aCacheId,
  524. nsTArray<nsID>& aDeletedBodyIdListOut)
  525. {
  526. MOZ_ASSERT(!NS_IsMainThread());
  527. MOZ_DIAGNOSTIC_ASSERT(aConn);
  528. // Delete the bodies explicitly as we need to read out the body IDs
  529. // anyway. These body IDs must be deleted one-by-one as content may
  530. // still be referencing them invidivually.
  531. AutoTArray<EntryId, 256> matches;
  532. nsresult rv = QueryAll(aConn, aCacheId, matches);
  533. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  534. AutoTArray<IdCount, 16> deletedSecurityIdList;
  535. rv = DeleteEntries(aConn, matches, aDeletedBodyIdListOut,
  536. deletedSecurityIdList);
  537. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  538. rv = DeleteSecurityInfoList(aConn, deletedSecurityIdList);
  539. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  540. // Delete the remainder of the cache using cascade semantics.
  541. nsCOMPtr<mozIStorageStatement> state;
  542. rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  543. "DELETE FROM caches WHERE id=:id;"
  544. ), getter_AddRefs(state));
  545. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  546. rv = state->BindInt64ByName(NS_LITERAL_CSTRING("id"), aCacheId);
  547. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  548. rv = state->Execute();
  549. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  550. return rv;
  551. }
  552. nsresult
  553. IsCacheOrphaned(mozIStorageConnection* aConn, CacheId aCacheId,
  554. bool* aOrphanedOut)
  555. {
  556. MOZ_ASSERT(!NS_IsMainThread());
  557. MOZ_DIAGNOSTIC_ASSERT(aConn);
  558. MOZ_DIAGNOSTIC_ASSERT(aOrphanedOut);
  559. // err on the side of not deleting user data
  560. *aOrphanedOut = false;
  561. nsCOMPtr<mozIStorageStatement> state;
  562. nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  563. "SELECT COUNT(*) FROM storage WHERE cache_id=:cache_id;"
  564. ), getter_AddRefs(state));
  565. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  566. rv = state->BindInt64ByName(NS_LITERAL_CSTRING("cache_id"), aCacheId);
  567. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  568. bool hasMoreData = false;
  569. rv = state->ExecuteStep(&hasMoreData);
  570. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  571. MOZ_DIAGNOSTIC_ASSERT(hasMoreData);
  572. int32_t refCount;
  573. rv = state->GetInt32(0, &refCount);
  574. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  575. *aOrphanedOut = refCount == 0;
  576. return rv;
  577. }
  578. nsresult
  579. FindOrphanedCacheIds(mozIStorageConnection* aConn,
  580. nsTArray<CacheId>& aOrphanedListOut)
  581. {
  582. nsCOMPtr<mozIStorageStatement> state;
  583. nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  584. "SELECT id FROM caches "
  585. "WHERE id NOT IN (SELECT cache_id from storage);"
  586. ), getter_AddRefs(state));
  587. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  588. bool hasMoreData = false;
  589. while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
  590. CacheId cacheId = INVALID_CACHE_ID;
  591. rv = state->GetInt64(0, &cacheId);
  592. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  593. aOrphanedListOut.AppendElement(cacheId);
  594. }
  595. return rv;
  596. }
  597. nsresult
  598. GetKnownBodyIds(mozIStorageConnection* aConn, nsTArray<nsID>& aBodyIdListOut)
  599. {
  600. MOZ_ASSERT(!NS_IsMainThread());
  601. MOZ_DIAGNOSTIC_ASSERT(aConn);
  602. nsCOMPtr<mozIStorageStatement> state;
  603. nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  604. "SELECT request_body_id, response_body_id FROM entries;"
  605. ), getter_AddRefs(state));
  606. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  607. bool hasMoreData = false;
  608. while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
  609. // extract 0 to 2 nsID structs per row
  610. for (uint32_t i = 0; i < 2; ++i) {
  611. bool isNull = false;
  612. rv = state->GetIsNull(i, &isNull);
  613. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  614. if (!isNull) {
  615. nsID id;
  616. rv = ExtractId(state, i, &id);
  617. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  618. aBodyIdListOut.AppendElement(id);
  619. }
  620. }
  621. }
  622. return rv;
  623. }
  624. nsresult
  625. CacheMatch(mozIStorageConnection* aConn, CacheId aCacheId,
  626. const CacheRequest& aRequest,
  627. const CacheQueryParams& aParams,
  628. bool* aFoundResponseOut,
  629. SavedResponse* aSavedResponseOut)
  630. {
  631. MOZ_ASSERT(!NS_IsMainThread());
  632. MOZ_DIAGNOSTIC_ASSERT(aConn);
  633. MOZ_DIAGNOSTIC_ASSERT(aFoundResponseOut);
  634. MOZ_DIAGNOSTIC_ASSERT(aSavedResponseOut);
  635. *aFoundResponseOut = false;
  636. AutoTArray<EntryId, 1> matches;
  637. nsresult rv = QueryCache(aConn, aCacheId, aRequest, aParams, matches, 1);
  638. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  639. if (matches.IsEmpty()) {
  640. return rv;
  641. }
  642. rv = ReadResponse(aConn, matches[0], aSavedResponseOut);
  643. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  644. aSavedResponseOut->mCacheId = aCacheId;
  645. *aFoundResponseOut = true;
  646. return rv;
  647. }
  648. nsresult
  649. CacheMatchAll(mozIStorageConnection* aConn, CacheId aCacheId,
  650. const CacheRequestOrVoid& aRequestOrVoid,
  651. const CacheQueryParams& aParams,
  652. nsTArray<SavedResponse>& aSavedResponsesOut)
  653. {
  654. MOZ_ASSERT(!NS_IsMainThread());
  655. MOZ_DIAGNOSTIC_ASSERT(aConn);
  656. nsresult rv;
  657. AutoTArray<EntryId, 256> matches;
  658. if (aRequestOrVoid.type() == CacheRequestOrVoid::Tvoid_t) {
  659. rv = QueryAll(aConn, aCacheId, matches);
  660. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  661. } else {
  662. rv = QueryCache(aConn, aCacheId, aRequestOrVoid, aParams, matches);
  663. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  664. }
  665. // TODO: replace this with a bulk load using SQL IN clause (bug 1110458)
  666. for (uint32_t i = 0; i < matches.Length(); ++i) {
  667. SavedResponse savedResponse;
  668. rv = ReadResponse(aConn, matches[i], &savedResponse);
  669. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  670. savedResponse.mCacheId = aCacheId;
  671. aSavedResponsesOut.AppendElement(savedResponse);
  672. }
  673. return rv;
  674. }
  675. nsresult
  676. CachePut(mozIStorageConnection* aConn, CacheId aCacheId,
  677. const CacheRequest& aRequest,
  678. const nsID* aRequestBodyId,
  679. const CacheResponse& aResponse,
  680. const nsID* aResponseBodyId,
  681. nsTArray<nsID>& aDeletedBodyIdListOut)
  682. {
  683. MOZ_ASSERT(!NS_IsMainThread());
  684. MOZ_DIAGNOSTIC_ASSERT(aConn);
  685. CacheQueryParams params(false, false, false, false,
  686. NS_LITERAL_STRING(""));
  687. AutoTArray<EntryId, 256> matches;
  688. nsresult rv = QueryCache(aConn, aCacheId, aRequest, params, matches);
  689. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  690. AutoTArray<IdCount, 16> deletedSecurityIdList;
  691. rv = DeleteEntries(aConn, matches, aDeletedBodyIdListOut,
  692. deletedSecurityIdList);
  693. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  694. rv = InsertEntry(aConn, aCacheId, aRequest, aRequestBodyId, aResponse,
  695. aResponseBodyId);
  696. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  697. // Delete the security values after doing the insert to avoid churning
  698. // the security table when its not necessary.
  699. rv = DeleteSecurityInfoList(aConn, deletedSecurityIdList);
  700. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  701. return rv;
  702. }
  703. nsresult
  704. CacheDelete(mozIStorageConnection* aConn, CacheId aCacheId,
  705. const CacheRequest& aRequest,
  706. const CacheQueryParams& aParams,
  707. nsTArray<nsID>& aDeletedBodyIdListOut, bool* aSuccessOut)
  708. {
  709. MOZ_ASSERT(!NS_IsMainThread());
  710. MOZ_DIAGNOSTIC_ASSERT(aConn);
  711. MOZ_DIAGNOSTIC_ASSERT(aSuccessOut);
  712. *aSuccessOut = false;
  713. AutoTArray<EntryId, 256> matches;
  714. nsresult rv = QueryCache(aConn, aCacheId, aRequest, aParams, matches);
  715. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  716. if (matches.IsEmpty()) {
  717. return rv;
  718. }
  719. AutoTArray<IdCount, 16> deletedSecurityIdList;
  720. rv = DeleteEntries(aConn, matches, aDeletedBodyIdListOut,
  721. deletedSecurityIdList);
  722. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  723. rv = DeleteSecurityInfoList(aConn, deletedSecurityIdList);
  724. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  725. *aSuccessOut = true;
  726. return rv;
  727. }
  728. nsresult
  729. CacheKeys(mozIStorageConnection* aConn, CacheId aCacheId,
  730. const CacheRequestOrVoid& aRequestOrVoid,
  731. const CacheQueryParams& aParams,
  732. nsTArray<SavedRequest>& aSavedRequestsOut)
  733. {
  734. MOZ_ASSERT(!NS_IsMainThread());
  735. MOZ_DIAGNOSTIC_ASSERT(aConn);
  736. nsresult rv;
  737. AutoTArray<EntryId, 256> matches;
  738. if (aRequestOrVoid.type() == CacheRequestOrVoid::Tvoid_t) {
  739. rv = QueryAll(aConn, aCacheId, matches);
  740. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  741. } else {
  742. rv = QueryCache(aConn, aCacheId, aRequestOrVoid, aParams, matches);
  743. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  744. }
  745. // TODO: replace this with a bulk load using SQL IN clause (bug 1110458)
  746. for (uint32_t i = 0; i < matches.Length(); ++i) {
  747. SavedRequest savedRequest;
  748. rv = ReadRequest(aConn, matches[i], &savedRequest);
  749. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  750. savedRequest.mCacheId = aCacheId;
  751. aSavedRequestsOut.AppendElement(savedRequest);
  752. }
  753. return rv;
  754. }
  755. nsresult
  756. StorageMatch(mozIStorageConnection* aConn,
  757. Namespace aNamespace,
  758. const CacheRequest& aRequest,
  759. const CacheQueryParams& aParams,
  760. bool* aFoundResponseOut,
  761. SavedResponse* aSavedResponseOut)
  762. {
  763. MOZ_ASSERT(!NS_IsMainThread());
  764. MOZ_DIAGNOSTIC_ASSERT(aConn);
  765. MOZ_DIAGNOSTIC_ASSERT(aFoundResponseOut);
  766. MOZ_DIAGNOSTIC_ASSERT(aSavedResponseOut);
  767. *aFoundResponseOut = false;
  768. nsresult rv;
  769. // If we are given a cache to check, then simply find its cache ID
  770. // and perform the match.
  771. if (!aParams.cacheName().EqualsLiteral("")) {
  772. bool foundCache = false;
  773. // no invalid CacheId, init to least likely real value
  774. CacheId cacheId = INVALID_CACHE_ID;
  775. rv = StorageGetCacheId(aConn, aNamespace, aParams.cacheName(), &foundCache,
  776. &cacheId);
  777. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  778. if (!foundCache) { return NS_OK; }
  779. rv = CacheMatch(aConn, cacheId, aRequest, aParams, aFoundResponseOut,
  780. aSavedResponseOut);
  781. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  782. return rv;
  783. }
  784. // Otherwise we need to get a list of all the cache IDs in this namespace.
  785. nsCOMPtr<mozIStorageStatement> state;
  786. rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  787. "SELECT cache_id FROM storage WHERE namespace=:namespace ORDER BY rowid;"
  788. ), getter_AddRefs(state));
  789. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  790. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("namespace"), aNamespace);
  791. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  792. AutoTArray<CacheId, 32> cacheIdList;
  793. bool hasMoreData = false;
  794. while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
  795. CacheId cacheId = INVALID_CACHE_ID;
  796. rv = state->GetInt64(0, &cacheId);
  797. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  798. cacheIdList.AppendElement(cacheId);
  799. }
  800. // Now try to find a match in each cache in order
  801. for (uint32_t i = 0; i < cacheIdList.Length(); ++i) {
  802. rv = CacheMatch(aConn, cacheIdList[i], aRequest, aParams, aFoundResponseOut,
  803. aSavedResponseOut);
  804. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  805. if (*aFoundResponseOut) {
  806. aSavedResponseOut->mCacheId = cacheIdList[i];
  807. return rv;
  808. }
  809. }
  810. return NS_OK;
  811. }
  812. nsresult
  813. StorageGetCacheId(mozIStorageConnection* aConn, Namespace aNamespace,
  814. const nsAString& aKey, bool* aFoundCacheOut,
  815. CacheId* aCacheIdOut)
  816. {
  817. MOZ_ASSERT(!NS_IsMainThread());
  818. MOZ_DIAGNOSTIC_ASSERT(aConn);
  819. MOZ_DIAGNOSTIC_ASSERT(aFoundCacheOut);
  820. MOZ_DIAGNOSTIC_ASSERT(aCacheIdOut);
  821. *aFoundCacheOut = false;
  822. // How we constrain the key column depends on the value of our key. Use
  823. // a format string for the query and let CreateAndBindKeyStatement() fill
  824. // it in for us.
  825. const char* query = "SELECT cache_id FROM storage "
  826. "WHERE namespace=:namespace AND %s "
  827. "ORDER BY rowid;";
  828. nsCOMPtr<mozIStorageStatement> state;
  829. nsresult rv = CreateAndBindKeyStatement(aConn, query, aKey,
  830. getter_AddRefs(state));
  831. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  832. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("namespace"), aNamespace);
  833. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  834. bool hasMoreData = false;
  835. rv = state->ExecuteStep(&hasMoreData);
  836. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  837. if (!hasMoreData) {
  838. return rv;
  839. }
  840. rv = state->GetInt64(0, aCacheIdOut);
  841. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  842. *aFoundCacheOut = true;
  843. return rv;
  844. }
  845. nsresult
  846. StoragePutCache(mozIStorageConnection* aConn, Namespace aNamespace,
  847. const nsAString& aKey, CacheId aCacheId)
  848. {
  849. MOZ_ASSERT(!NS_IsMainThread());
  850. MOZ_DIAGNOSTIC_ASSERT(aConn);
  851. nsCOMPtr<mozIStorageStatement> state;
  852. nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  853. "INSERT INTO storage (namespace, key, cache_id) "
  854. "VALUES (:namespace, :key, :cache_id);"
  855. ), getter_AddRefs(state));
  856. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  857. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("namespace"), aNamespace);
  858. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  859. rv = state->BindStringAsBlobByName(NS_LITERAL_CSTRING("key"), aKey);
  860. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  861. rv = state->BindInt64ByName(NS_LITERAL_CSTRING("cache_id"), aCacheId);
  862. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  863. rv = state->Execute();
  864. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  865. return rv;
  866. }
  867. nsresult
  868. StorageForgetCache(mozIStorageConnection* aConn, Namespace aNamespace,
  869. const nsAString& aKey)
  870. {
  871. MOZ_ASSERT(!NS_IsMainThread());
  872. MOZ_DIAGNOSTIC_ASSERT(aConn);
  873. // How we constrain the key column depends on the value of our key. Use
  874. // a format string for the query and let CreateAndBindKeyStatement() fill
  875. // it in for us.
  876. const char *query = "DELETE FROM storage WHERE namespace=:namespace AND %s;";
  877. nsCOMPtr<mozIStorageStatement> state;
  878. nsresult rv = CreateAndBindKeyStatement(aConn, query, aKey,
  879. getter_AddRefs(state));
  880. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  881. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("namespace"), aNamespace);
  882. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  883. rv = state->Execute();
  884. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  885. return rv;
  886. }
  887. nsresult
  888. StorageGetKeys(mozIStorageConnection* aConn, Namespace aNamespace,
  889. nsTArray<nsString>& aKeysOut)
  890. {
  891. MOZ_ASSERT(!NS_IsMainThread());
  892. MOZ_DIAGNOSTIC_ASSERT(aConn);
  893. nsCOMPtr<mozIStorageStatement> state;
  894. nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  895. "SELECT key FROM storage WHERE namespace=:namespace ORDER BY rowid;"
  896. ), getter_AddRefs(state));
  897. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  898. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("namespace"), aNamespace);
  899. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  900. bool hasMoreData = false;
  901. while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
  902. nsAutoString key;
  903. rv = state->GetBlobAsString(0, key);
  904. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  905. aKeysOut.AppendElement(key);
  906. }
  907. return rv;
  908. }
  909. namespace {
  910. nsresult
  911. QueryAll(mozIStorageConnection* aConn, CacheId aCacheId,
  912. nsTArray<EntryId>& aEntryIdListOut)
  913. {
  914. MOZ_ASSERT(!NS_IsMainThread());
  915. MOZ_DIAGNOSTIC_ASSERT(aConn);
  916. nsCOMPtr<mozIStorageStatement> state;
  917. nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  918. "SELECT id FROM entries WHERE cache_id=:cache_id ORDER BY id;"
  919. ), getter_AddRefs(state));
  920. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  921. rv = state->BindInt64ByName(NS_LITERAL_CSTRING("cache_id"), aCacheId);
  922. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  923. bool hasMoreData = false;
  924. while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
  925. EntryId entryId = INT32_MAX;
  926. rv = state->GetInt32(0, &entryId);
  927. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  928. aEntryIdListOut.AppendElement(entryId);
  929. }
  930. return rv;
  931. }
  932. nsresult
  933. QueryCache(mozIStorageConnection* aConn, CacheId aCacheId,
  934. const CacheRequest& aRequest,
  935. const CacheQueryParams& aParams,
  936. nsTArray<EntryId>& aEntryIdListOut,
  937. uint32_t aMaxResults)
  938. {
  939. MOZ_ASSERT(!NS_IsMainThread());
  940. MOZ_DIAGNOSTIC_ASSERT(aConn);
  941. MOZ_DIAGNOSTIC_ASSERT(aMaxResults > 0);
  942. if (!aParams.ignoreMethod() && !aRequest.method().LowerCaseEqualsLiteral("get")
  943. && !aRequest.method().LowerCaseEqualsLiteral("head"))
  944. {
  945. return NS_OK;
  946. }
  947. nsAutoCString query(
  948. "SELECT id, COUNT(response_headers.name) AS vary_count "
  949. "FROM entries "
  950. "LEFT OUTER JOIN response_headers ON entries.id=response_headers.entry_id "
  951. "AND response_headers.name='vary' "
  952. "WHERE entries.cache_id=:cache_id "
  953. "AND entries.request_url_no_query_hash=:url_no_query_hash "
  954. );
  955. if (!aParams.ignoreSearch()) {
  956. query.AppendLiteral("AND entries.request_url_query_hash=:url_query_hash ");
  957. }
  958. query.AppendLiteral("AND entries.request_url_no_query=:url_no_query ");
  959. if (!aParams.ignoreSearch()) {
  960. query.AppendLiteral("AND entries.request_url_query=:url_query ");
  961. }
  962. query.AppendLiteral("GROUP BY entries.id ORDER BY entries.id;");
  963. nsCOMPtr<mozIStorageStatement> state;
  964. nsresult rv = aConn->CreateStatement(query, getter_AddRefs(state));
  965. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  966. rv = state->BindInt64ByName(NS_LITERAL_CSTRING("cache_id"), aCacheId);
  967. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  968. nsCOMPtr<nsICryptoHash> crypto =
  969. do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
  970. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  971. nsAutoCString urlWithoutQueryHash;
  972. rv = HashCString(crypto, aRequest.urlWithoutQuery(), urlWithoutQueryHash);
  973. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  974. rv = state->BindUTF8StringAsBlobByName(NS_LITERAL_CSTRING("url_no_query_hash"),
  975. urlWithoutQueryHash);
  976. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  977. if (!aParams.ignoreSearch()) {
  978. nsAutoCString urlQueryHash;
  979. rv = HashCString(crypto, aRequest.urlQuery(), urlQueryHash);
  980. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  981. rv = state->BindUTF8StringAsBlobByName(NS_LITERAL_CSTRING("url_query_hash"),
  982. urlQueryHash);
  983. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  984. }
  985. rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("url_no_query"),
  986. aRequest.urlWithoutQuery());
  987. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  988. if (!aParams.ignoreSearch()) {
  989. rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("url_query"),
  990. aRequest.urlQuery());
  991. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  992. }
  993. bool hasMoreData = false;
  994. while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
  995. // no invalid EntryId, init to least likely real value
  996. EntryId entryId = INT32_MAX;
  997. rv = state->GetInt32(0, &entryId);
  998. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  999. int32_t varyCount;
  1000. rv = state->GetInt32(1, &varyCount);
  1001. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1002. if (!aParams.ignoreVary() && varyCount > 0) {
  1003. bool matchedByVary = false;
  1004. rv = MatchByVaryHeader(aConn, aRequest, entryId, &matchedByVary);
  1005. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1006. if (!matchedByVary) {
  1007. continue;
  1008. }
  1009. }
  1010. aEntryIdListOut.AppendElement(entryId);
  1011. if (aEntryIdListOut.Length() == aMaxResults) {
  1012. return NS_OK;
  1013. }
  1014. }
  1015. return rv;
  1016. }
  1017. nsresult
  1018. MatchByVaryHeader(mozIStorageConnection* aConn,
  1019. const CacheRequest& aRequest,
  1020. EntryId entryId, bool* aSuccessOut)
  1021. {
  1022. MOZ_ASSERT(!NS_IsMainThread());
  1023. MOZ_DIAGNOSTIC_ASSERT(aConn);
  1024. *aSuccessOut = false;
  1025. nsCOMPtr<mozIStorageStatement> state;
  1026. nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  1027. "SELECT value FROM response_headers "
  1028. "WHERE name='vary' AND entry_id=:entry_id;"
  1029. ), getter_AddRefs(state));
  1030. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1031. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("entry_id"), entryId);
  1032. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1033. AutoTArray<nsCString, 8> varyValues;
  1034. bool hasMoreData = false;
  1035. while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
  1036. nsAutoCString value;
  1037. rv = state->GetUTF8String(0, value);
  1038. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1039. varyValues.AppendElement(value);
  1040. }
  1041. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1042. // Should not have called this function if this was not the case
  1043. MOZ_DIAGNOSTIC_ASSERT(!varyValues.IsEmpty());
  1044. state->Reset();
  1045. rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  1046. "SELECT name, value FROM request_headers "
  1047. "WHERE entry_id=:entry_id;"
  1048. ), getter_AddRefs(state));
  1049. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1050. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("entry_id"), entryId);
  1051. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1052. RefPtr<InternalHeaders> cachedHeaders =
  1053. new InternalHeaders(HeadersGuardEnum::None);
  1054. while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
  1055. nsAutoCString name;
  1056. nsAutoCString value;
  1057. rv = state->GetUTF8String(0, name);
  1058. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1059. rv = state->GetUTF8String(1, value);
  1060. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1061. ErrorResult errorResult;
  1062. cachedHeaders->Append(name, value, errorResult);
  1063. if (errorResult.Failed()) { return errorResult.StealNSResult(); }
  1064. }
  1065. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1066. RefPtr<InternalHeaders> queryHeaders =
  1067. TypeUtils::ToInternalHeaders(aRequest.headers());
  1068. // Assume the vary headers match until we find a conflict
  1069. bool varyHeadersMatch = true;
  1070. for (uint32_t i = 0; i < varyValues.Length(); ++i) {
  1071. // Extract the header names inside the Vary header value.
  1072. nsAutoCString varyValue(varyValues[i]);
  1073. char* rawBuffer = varyValue.BeginWriting();
  1074. char* token = nsCRT::strtok(rawBuffer, NS_HTTP_HEADER_SEPS, &rawBuffer);
  1075. bool bailOut = false;
  1076. for (; token;
  1077. token = nsCRT::strtok(rawBuffer, NS_HTTP_HEADER_SEPS, &rawBuffer)) {
  1078. nsDependentCString header(token);
  1079. MOZ_DIAGNOSTIC_ASSERT(!header.EqualsLiteral("*"),
  1080. "We should have already caught this in "
  1081. "TypeUtils::ToPCacheResponseWithoutBody()");
  1082. ErrorResult errorResult;
  1083. nsAutoCString queryValue;
  1084. queryHeaders->Get(header, queryValue, errorResult);
  1085. if (errorResult.Failed()) {
  1086. errorResult.SuppressException();
  1087. MOZ_DIAGNOSTIC_ASSERT(queryValue.IsEmpty());
  1088. }
  1089. nsAutoCString cachedValue;
  1090. cachedHeaders->Get(header, cachedValue, errorResult);
  1091. if (errorResult.Failed()) {
  1092. errorResult.SuppressException();
  1093. MOZ_DIAGNOSTIC_ASSERT(cachedValue.IsEmpty());
  1094. }
  1095. if (queryValue != cachedValue) {
  1096. varyHeadersMatch = false;
  1097. bailOut = true;
  1098. break;
  1099. }
  1100. }
  1101. if (bailOut) {
  1102. break;
  1103. }
  1104. }
  1105. *aSuccessOut = varyHeadersMatch;
  1106. return rv;
  1107. }
  1108. nsresult
  1109. DeleteEntries(mozIStorageConnection* aConn,
  1110. const nsTArray<EntryId>& aEntryIdList,
  1111. nsTArray<nsID>& aDeletedBodyIdListOut,
  1112. nsTArray<IdCount>& aDeletedSecurityIdListOut,
  1113. uint32_t aPos, int32_t aLen)
  1114. {
  1115. MOZ_ASSERT(!NS_IsMainThread());
  1116. MOZ_DIAGNOSTIC_ASSERT(aConn);
  1117. if (aEntryIdList.IsEmpty()) {
  1118. return NS_OK;
  1119. }
  1120. MOZ_DIAGNOSTIC_ASSERT(aPos < aEntryIdList.Length());
  1121. if (aLen < 0) {
  1122. aLen = aEntryIdList.Length() - aPos;
  1123. }
  1124. // Sqlite limits the number of entries allowed for an IN clause,
  1125. // so split up larger operations.
  1126. if (aLen > kMaxEntriesPerStatement) {
  1127. uint32_t curPos = aPos;
  1128. int32_t remaining = aLen;
  1129. while (remaining > 0) {
  1130. int32_t max = kMaxEntriesPerStatement;
  1131. int32_t curLen = std::min(max, remaining);
  1132. nsresult rv = DeleteEntries(aConn, aEntryIdList, aDeletedBodyIdListOut,
  1133. aDeletedSecurityIdListOut, curPos, curLen);
  1134. if (NS_FAILED(rv)) { return rv; }
  1135. curPos += curLen;
  1136. remaining -= curLen;
  1137. }
  1138. return NS_OK;
  1139. }
  1140. nsCOMPtr<mozIStorageStatement> state;
  1141. nsAutoCString query(
  1142. "SELECT request_body_id, response_body_id, response_security_info_id "
  1143. "FROM entries WHERE id IN ("
  1144. );
  1145. AppendListParamsToQuery(query, aEntryIdList, aPos, aLen);
  1146. query.AppendLiteral(")");
  1147. nsresult rv = aConn->CreateStatement(query, getter_AddRefs(state));
  1148. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1149. rv = BindListParamsToQuery(state, aEntryIdList, aPos, aLen);
  1150. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1151. bool hasMoreData = false;
  1152. while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
  1153. // extract 0 to 2 nsID structs per row
  1154. for (uint32_t i = 0; i < 2; ++i) {
  1155. bool isNull = false;
  1156. rv = state->GetIsNull(i, &isNull);
  1157. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1158. if (!isNull) {
  1159. nsID id;
  1160. rv = ExtractId(state, i, &id);
  1161. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1162. aDeletedBodyIdListOut.AppendElement(id);
  1163. }
  1164. }
  1165. // and then a possible third entry for the security id
  1166. bool isNull = false;
  1167. rv = state->GetIsNull(2, &isNull);
  1168. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1169. if (!isNull) {
  1170. int32_t securityId = -1;
  1171. rv = state->GetInt32(2, &securityId);
  1172. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1173. // First try to increment the count for this ID if we're already
  1174. // seen it
  1175. bool found = false;
  1176. for (uint32_t i = 0; i < aDeletedSecurityIdListOut.Length(); ++i) {
  1177. if (aDeletedSecurityIdListOut[i].mId == securityId) {
  1178. found = true;
  1179. aDeletedSecurityIdListOut[i].mCount += 1;
  1180. break;
  1181. }
  1182. }
  1183. // Otherwise add a new entry for this ID with a count of 1
  1184. if (!found) {
  1185. aDeletedSecurityIdListOut.AppendElement(IdCount(securityId));
  1186. }
  1187. }
  1188. }
  1189. // Dependent records removed via ON DELETE CASCADE
  1190. query = NS_LITERAL_CSTRING(
  1191. "DELETE FROM entries WHERE id IN ("
  1192. );
  1193. AppendListParamsToQuery(query, aEntryIdList, aPos, aLen);
  1194. query.AppendLiteral(")");
  1195. rv = aConn->CreateStatement(query, getter_AddRefs(state));
  1196. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1197. rv = BindListParamsToQuery(state, aEntryIdList, aPos, aLen);
  1198. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1199. rv = state->Execute();
  1200. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1201. return rv;
  1202. }
  1203. nsresult
  1204. InsertSecurityInfo(mozIStorageConnection* aConn, nsICryptoHash* aCrypto,
  1205. const nsACString& aData, int32_t *aIdOut)
  1206. {
  1207. MOZ_DIAGNOSTIC_ASSERT(aConn);
  1208. MOZ_DIAGNOSTIC_ASSERT(aCrypto);
  1209. MOZ_DIAGNOSTIC_ASSERT(aIdOut);
  1210. MOZ_DIAGNOSTIC_ASSERT(!aData.IsEmpty());
  1211. // We want to use an index to find existing security blobs, but indexing
  1212. // the full blob would be quite expensive. Instead, we index a small
  1213. // hash value. Calculate this hash as the first 8 bytes of the SHA1 of
  1214. // the full data.
  1215. nsAutoCString hash;
  1216. nsresult rv = HashCString(aCrypto, aData, hash);
  1217. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1218. // Next, search for an existing entry for this blob by comparing the hash
  1219. // value first and then the full data. SQLite is smart enough to use
  1220. // the index on the hash to search the table before doing the expensive
  1221. // comparison of the large data column. (This was verified with EXPLAIN.)
  1222. nsCOMPtr<mozIStorageStatement> state;
  1223. rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  1224. // Note that hash and data are blobs, but we can use = here since the
  1225. // columns are NOT NULL.
  1226. "SELECT id, refcount FROM security_info WHERE hash=:hash AND data=:data;"
  1227. ), getter_AddRefs(state));
  1228. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1229. rv = state->BindUTF8StringAsBlobByName(NS_LITERAL_CSTRING("hash"), hash);
  1230. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1231. rv = state->BindUTF8StringAsBlobByName(NS_LITERAL_CSTRING("data"), aData);
  1232. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1233. bool hasMoreData = false;
  1234. rv = state->ExecuteStep(&hasMoreData);
  1235. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1236. // This security info blob is already in the database
  1237. if (hasMoreData) {
  1238. // get the existing security blob id to return
  1239. rv = state->GetInt32(0, aIdOut);
  1240. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1241. int32_t refcount = -1;
  1242. rv = state->GetInt32(1, &refcount);
  1243. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1244. // But first, update the refcount in the database.
  1245. refcount += 1;
  1246. rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  1247. "UPDATE security_info SET refcount=:refcount WHERE id=:id;"
  1248. ), getter_AddRefs(state));
  1249. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1250. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("refcount"), refcount);
  1251. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1252. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("id"), *aIdOut);
  1253. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1254. rv = state->Execute();
  1255. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1256. return NS_OK;
  1257. }
  1258. // This is a new security info blob. Create a new row in the security table
  1259. // with an initial refcount of 1.
  1260. rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  1261. "INSERT INTO security_info (hash, data, refcount) VALUES (:hash, :data, 1);"
  1262. ), getter_AddRefs(state));
  1263. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1264. rv = state->BindUTF8StringAsBlobByName(NS_LITERAL_CSTRING("hash"), hash);
  1265. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1266. rv = state->BindUTF8StringAsBlobByName(NS_LITERAL_CSTRING("data"), aData);
  1267. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1268. rv = state->Execute();
  1269. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1270. rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  1271. "SELECT last_insert_rowid()"
  1272. ), getter_AddRefs(state));
  1273. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1274. hasMoreData = false;
  1275. rv = state->ExecuteStep(&hasMoreData);
  1276. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1277. rv = state->GetInt32(0, aIdOut);
  1278. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1279. return NS_OK;
  1280. }
  1281. nsresult
  1282. DeleteSecurityInfo(mozIStorageConnection* aConn, int32_t aId, int32_t aCount)
  1283. {
  1284. // First, we need to determine the current refcount for this security blob.
  1285. nsCOMPtr<mozIStorageStatement> state;
  1286. nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  1287. "SELECT refcount FROM security_info WHERE id=:id;"
  1288. ), getter_AddRefs(state));
  1289. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1290. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("id"), aId);
  1291. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1292. bool hasMoreData = false;
  1293. rv = state->ExecuteStep(&hasMoreData);
  1294. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1295. int32_t refcount = -1;
  1296. rv = state->GetInt32(0, &refcount);
  1297. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1298. MOZ_DIAGNOSTIC_ASSERT(refcount >= aCount);
  1299. // Next, calculate the new refcount
  1300. int32_t newCount = refcount - aCount;
  1301. // If the last reference to this security blob was removed we can
  1302. // just remove the entire row.
  1303. if (newCount == 0) {
  1304. rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  1305. "DELETE FROM security_info WHERE id=:id;"
  1306. ), getter_AddRefs(state));
  1307. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1308. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("id"), aId);
  1309. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1310. rv = state->Execute();
  1311. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1312. return NS_OK;
  1313. }
  1314. // Otherwise update the refcount in the table to reflect the reduced
  1315. // number of references to the security blob.
  1316. rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  1317. "UPDATE security_info SET refcount=:refcount WHERE id=:id;"
  1318. ), getter_AddRefs(state));
  1319. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1320. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("refcount"), newCount);
  1321. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1322. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("id"), aId);
  1323. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1324. rv = state->Execute();
  1325. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1326. return NS_OK;
  1327. }
  1328. nsresult
  1329. DeleteSecurityInfoList(mozIStorageConnection* aConn,
  1330. const nsTArray<IdCount>& aDeletedStorageIdList)
  1331. {
  1332. for (uint32_t i = 0; i < aDeletedStorageIdList.Length(); ++i) {
  1333. nsresult rv = DeleteSecurityInfo(aConn, aDeletedStorageIdList[i].mId,
  1334. aDeletedStorageIdList[i].mCount);
  1335. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1336. }
  1337. return NS_OK;
  1338. }
  1339. nsresult
  1340. InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
  1341. const CacheRequest& aRequest,
  1342. const nsID* aRequestBodyId,
  1343. const CacheResponse& aResponse,
  1344. const nsID* aResponseBodyId)
  1345. {
  1346. MOZ_ASSERT(!NS_IsMainThread());
  1347. MOZ_DIAGNOSTIC_ASSERT(aConn);
  1348. nsresult rv = NS_OK;
  1349. nsCOMPtr<nsICryptoHash> crypto =
  1350. do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
  1351. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1352. int32_t securityId = -1;
  1353. if (!aResponse.channelInfo().securityInfo().IsEmpty()) {
  1354. rv = InsertSecurityInfo(aConn, crypto,
  1355. aResponse.channelInfo().securityInfo(),
  1356. &securityId);
  1357. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1358. }
  1359. nsCOMPtr<mozIStorageStatement> state;
  1360. rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  1361. "INSERT INTO entries ("
  1362. "request_method, "
  1363. "request_url_no_query, "
  1364. "request_url_no_query_hash, "
  1365. "request_url_query, "
  1366. "request_url_query_hash, "
  1367. "request_url_fragment, "
  1368. "request_referrer, "
  1369. "request_referrer_policy, "
  1370. "request_headers_guard, "
  1371. "request_mode, "
  1372. "request_credentials, "
  1373. "request_contentpolicytype, "
  1374. "request_cache, "
  1375. "request_redirect, "
  1376. "request_integrity, "
  1377. "request_body_id, "
  1378. "response_type, "
  1379. "response_status, "
  1380. "response_status_text, "
  1381. "response_headers_guard, "
  1382. "response_body_id, "
  1383. "response_security_info_id, "
  1384. "response_principal_info, "
  1385. "cache_id "
  1386. ") VALUES ("
  1387. ":request_method, "
  1388. ":request_url_no_query, "
  1389. ":request_url_no_query_hash, "
  1390. ":request_url_query, "
  1391. ":request_url_query_hash, "
  1392. ":request_url_fragment, "
  1393. ":request_referrer, "
  1394. ":request_referrer_policy, "
  1395. ":request_headers_guard, "
  1396. ":request_mode, "
  1397. ":request_credentials, "
  1398. ":request_contentpolicytype, "
  1399. ":request_cache, "
  1400. ":request_redirect, "
  1401. ":request_integrity, "
  1402. ":request_body_id, "
  1403. ":response_type, "
  1404. ":response_status, "
  1405. ":response_status_text, "
  1406. ":response_headers_guard, "
  1407. ":response_body_id, "
  1408. ":response_security_info_id, "
  1409. ":response_principal_info, "
  1410. ":cache_id "
  1411. ");"
  1412. ), getter_AddRefs(state));
  1413. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1414. rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("request_method"),
  1415. aRequest.method());
  1416. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1417. rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("request_url_no_query"),
  1418. aRequest.urlWithoutQuery());
  1419. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1420. nsAutoCString urlWithoutQueryHash;
  1421. rv = HashCString(crypto, aRequest.urlWithoutQuery(), urlWithoutQueryHash);
  1422. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1423. rv = state->BindUTF8StringAsBlobByName(
  1424. NS_LITERAL_CSTRING("request_url_no_query_hash"), urlWithoutQueryHash);
  1425. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1426. rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("request_url_query"),
  1427. aRequest.urlQuery());
  1428. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1429. nsAutoCString urlQueryHash;
  1430. rv = HashCString(crypto, aRequest.urlQuery(), urlQueryHash);
  1431. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1432. rv = state->BindUTF8StringAsBlobByName(
  1433. NS_LITERAL_CSTRING("request_url_query_hash"), urlQueryHash);
  1434. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1435. rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("request_url_fragment"),
  1436. aRequest.urlFragment());
  1437. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1438. rv = state->BindStringByName(NS_LITERAL_CSTRING("request_referrer"),
  1439. aRequest.referrer());
  1440. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1441. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_referrer_policy"),
  1442. static_cast<int32_t>(aRequest.referrerPolicy()));
  1443. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1444. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_headers_guard"),
  1445. static_cast<int32_t>(aRequest.headersGuard()));
  1446. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1447. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_mode"),
  1448. static_cast<int32_t>(aRequest.mode()));
  1449. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1450. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_credentials"),
  1451. static_cast<int32_t>(aRequest.credentials()));
  1452. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1453. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_contentpolicytype"),
  1454. static_cast<int32_t>(aRequest.contentPolicyType()));
  1455. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1456. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_cache"),
  1457. static_cast<int32_t>(aRequest.requestCache()));
  1458. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1459. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_redirect"),
  1460. static_cast<int32_t>(aRequest.requestRedirect()));
  1461. rv = state->BindStringByName(NS_LITERAL_CSTRING("request_integrity"),
  1462. aRequest.integrity());
  1463. rv = BindId(state, NS_LITERAL_CSTRING("request_body_id"), aRequestBodyId);
  1464. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1465. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("response_type"),
  1466. static_cast<int32_t>(aResponse.type()));
  1467. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1468. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("response_status"),
  1469. aResponse.status());
  1470. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1471. rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("response_status_text"),
  1472. aResponse.statusText());
  1473. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1474. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("response_headers_guard"),
  1475. static_cast<int32_t>(aResponse.headersGuard()));
  1476. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1477. rv = BindId(state, NS_LITERAL_CSTRING("response_body_id"), aResponseBodyId);
  1478. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1479. if (aResponse.channelInfo().securityInfo().IsEmpty()) {
  1480. rv = state->BindNullByName(NS_LITERAL_CSTRING("response_security_info_id"));
  1481. } else {
  1482. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("response_security_info_id"),
  1483. securityId);
  1484. }
  1485. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1486. nsAutoCString serializedInfo;
  1487. // We only allow content serviceworkers right now.
  1488. if (aResponse.principalInfo().type() == mozilla::ipc::OptionalPrincipalInfo::TPrincipalInfo) {
  1489. const mozilla::ipc::PrincipalInfo& principalInfo =
  1490. aResponse.principalInfo().get_PrincipalInfo();
  1491. MOZ_DIAGNOSTIC_ASSERT(principalInfo.type() == mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
  1492. const mozilla::ipc::ContentPrincipalInfo& cInfo =
  1493. principalInfo.get_ContentPrincipalInfo();
  1494. serializedInfo.Append(cInfo.spec());
  1495. nsAutoCString suffix;
  1496. cInfo.attrs().CreateSuffix(suffix);
  1497. serializedInfo.Append(suffix);
  1498. }
  1499. rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("response_principal_info"),
  1500. serializedInfo);
  1501. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1502. rv = state->BindInt64ByName(NS_LITERAL_CSTRING("cache_id"), aCacheId);
  1503. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1504. rv = state->Execute();
  1505. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1506. rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  1507. "SELECT last_insert_rowid()"
  1508. ), getter_AddRefs(state));
  1509. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1510. bool hasMoreData = false;
  1511. rv = state->ExecuteStep(&hasMoreData);
  1512. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1513. int32_t entryId;
  1514. rv = state->GetInt32(0, &entryId);
  1515. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1516. rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  1517. "INSERT INTO request_headers ("
  1518. "name, "
  1519. "value, "
  1520. "entry_id "
  1521. ") VALUES (:name, :value, :entry_id)"
  1522. ), getter_AddRefs(state));
  1523. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1524. const nsTArray<HeadersEntry>& requestHeaders = aRequest.headers();
  1525. for (uint32_t i = 0; i < requestHeaders.Length(); ++i) {
  1526. rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("name"),
  1527. requestHeaders[i].name());
  1528. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1529. rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("value"),
  1530. requestHeaders[i].value());
  1531. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1532. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("entry_id"), entryId);
  1533. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1534. rv = state->Execute();
  1535. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1536. }
  1537. rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  1538. "INSERT INTO response_headers ("
  1539. "name, "
  1540. "value, "
  1541. "entry_id "
  1542. ") VALUES (:name, :value, :entry_id)"
  1543. ), getter_AddRefs(state));
  1544. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1545. const nsTArray<HeadersEntry>& responseHeaders = aResponse.headers();
  1546. for (uint32_t i = 0; i < responseHeaders.Length(); ++i) {
  1547. rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("name"),
  1548. responseHeaders[i].name());
  1549. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1550. rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("value"),
  1551. responseHeaders[i].value());
  1552. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1553. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("entry_id"), entryId);
  1554. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1555. rv = state->Execute();
  1556. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1557. }
  1558. rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  1559. "INSERT INTO response_url_list ("
  1560. "url, "
  1561. "entry_id "
  1562. ") VALUES (:url, :entry_id)"
  1563. ), getter_AddRefs(state));
  1564. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1565. const nsTArray<nsCString>& responseUrlList = aResponse.urlList();
  1566. for (uint32_t i = 0; i < responseUrlList.Length(); ++i) {
  1567. rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("url"),
  1568. responseUrlList[i]);
  1569. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1570. rv = state->BindInt64ByName(NS_LITERAL_CSTRING("entry_id"), entryId);
  1571. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1572. rv = state->Execute();
  1573. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1574. }
  1575. return rv;
  1576. }
  1577. nsresult
  1578. ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
  1579. SavedResponse* aSavedResponseOut)
  1580. {
  1581. MOZ_ASSERT(!NS_IsMainThread());
  1582. MOZ_DIAGNOSTIC_ASSERT(aConn);
  1583. MOZ_DIAGNOSTIC_ASSERT(aSavedResponseOut);
  1584. nsCOMPtr<mozIStorageStatement> state;
  1585. nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  1586. "SELECT "
  1587. "entries.response_type, "
  1588. "entries.response_status, "
  1589. "entries.response_status_text, "
  1590. "entries.response_headers_guard, "
  1591. "entries.response_body_id, "
  1592. "entries.response_principal_info, "
  1593. "security_info.data "
  1594. "FROM entries "
  1595. "LEFT OUTER JOIN security_info "
  1596. "ON entries.response_security_info_id=security_info.id "
  1597. "WHERE entries.id=:id;"
  1598. ), getter_AddRefs(state));
  1599. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1600. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("id"), aEntryId);
  1601. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1602. bool hasMoreData = false;
  1603. rv = state->ExecuteStep(&hasMoreData);
  1604. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1605. int32_t type;
  1606. rv = state->GetInt32(0, &type);
  1607. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1608. aSavedResponseOut->mValue.type() = static_cast<ResponseType>(type);
  1609. int32_t status;
  1610. rv = state->GetInt32(1, &status);
  1611. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1612. aSavedResponseOut->mValue.status() = status;
  1613. rv = state->GetUTF8String(2, aSavedResponseOut->mValue.statusText());
  1614. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1615. int32_t guard;
  1616. rv = state->GetInt32(3, &guard);
  1617. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1618. aSavedResponseOut->mValue.headersGuard() =
  1619. static_cast<HeadersGuardEnum>(guard);
  1620. bool nullBody = false;
  1621. rv = state->GetIsNull(4, &nullBody);
  1622. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1623. aSavedResponseOut->mHasBodyId = !nullBody;
  1624. if (aSavedResponseOut->mHasBodyId) {
  1625. rv = ExtractId(state, 4, &aSavedResponseOut->mBodyId);
  1626. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1627. }
  1628. nsAutoCString serializedInfo;
  1629. rv = state->GetUTF8String(5, serializedInfo);
  1630. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1631. aSavedResponseOut->mValue.principalInfo() = void_t();
  1632. if (!serializedInfo.IsEmpty()) {
  1633. nsAutoCString specNoSuffix;
  1634. PrincipalOriginAttributes attrs;
  1635. if (!attrs.PopulateFromOrigin(serializedInfo, specNoSuffix)) {
  1636. NS_WARNING("Something went wrong parsing a serialized principal!");
  1637. return NS_ERROR_FAILURE;
  1638. }
  1639. aSavedResponseOut->mValue.principalInfo() =
  1640. mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), specNoSuffix);
  1641. }
  1642. rv = state->GetBlobAsUTF8String(6, aSavedResponseOut->mValue.channelInfo().securityInfo());
  1643. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1644. rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  1645. "SELECT "
  1646. "name, "
  1647. "value "
  1648. "FROM response_headers "
  1649. "WHERE entry_id=:entry_id;"
  1650. ), getter_AddRefs(state));
  1651. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1652. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("entry_id"), aEntryId);
  1653. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1654. while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
  1655. HeadersEntry header;
  1656. rv = state->GetUTF8String(0, header.name());
  1657. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1658. rv = state->GetUTF8String(1, header.value());
  1659. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1660. aSavedResponseOut->mValue.headers().AppendElement(header);
  1661. }
  1662. rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  1663. "SELECT "
  1664. "url "
  1665. "FROM response_url_list "
  1666. "WHERE entry_id=:entry_id;"
  1667. ), getter_AddRefs(state));
  1668. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1669. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("entry_id"), aEntryId);
  1670. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1671. while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
  1672. nsCString url;
  1673. rv = state->GetUTF8String(0, url);
  1674. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1675. aSavedResponseOut->mValue.urlList().AppendElement(url);
  1676. }
  1677. return rv;
  1678. }
  1679. nsresult
  1680. ReadRequest(mozIStorageConnection* aConn, EntryId aEntryId,
  1681. SavedRequest* aSavedRequestOut)
  1682. {
  1683. MOZ_ASSERT(!NS_IsMainThread());
  1684. MOZ_DIAGNOSTIC_ASSERT(aConn);
  1685. MOZ_DIAGNOSTIC_ASSERT(aSavedRequestOut);
  1686. nsCOMPtr<mozIStorageStatement> state;
  1687. nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  1688. "SELECT "
  1689. "request_method, "
  1690. "request_url_no_query, "
  1691. "request_url_query, "
  1692. "request_url_fragment, "
  1693. "request_referrer, "
  1694. "request_referrer_policy, "
  1695. "request_headers_guard, "
  1696. "request_mode, "
  1697. "request_credentials, "
  1698. "request_contentpolicytype, "
  1699. "request_cache, "
  1700. "request_redirect, "
  1701. "request_integrity, "
  1702. "request_body_id "
  1703. "FROM entries "
  1704. "WHERE id=:id;"
  1705. ), getter_AddRefs(state));
  1706. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1707. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("id"), aEntryId);
  1708. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1709. bool hasMoreData = false;
  1710. rv = state->ExecuteStep(&hasMoreData);
  1711. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1712. rv = state->GetUTF8String(0, aSavedRequestOut->mValue.method());
  1713. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1714. rv = state->GetUTF8String(1, aSavedRequestOut->mValue.urlWithoutQuery());
  1715. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1716. rv = state->GetUTF8String(2, aSavedRequestOut->mValue.urlQuery());
  1717. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1718. rv = state->GetUTF8String(3, aSavedRequestOut->mValue.urlFragment());
  1719. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1720. rv = state->GetString(4, aSavedRequestOut->mValue.referrer());
  1721. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1722. int32_t referrerPolicy;
  1723. rv = state->GetInt32(5, &referrerPolicy);
  1724. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1725. aSavedRequestOut->mValue.referrerPolicy() =
  1726. static_cast<ReferrerPolicy>(referrerPolicy);
  1727. int32_t guard;
  1728. rv = state->GetInt32(6, &guard);
  1729. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1730. aSavedRequestOut->mValue.headersGuard() =
  1731. static_cast<HeadersGuardEnum>(guard);
  1732. int32_t mode;
  1733. rv = state->GetInt32(7, &mode);
  1734. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1735. aSavedRequestOut->mValue.mode() = static_cast<RequestMode>(mode);
  1736. int32_t credentials;
  1737. rv = state->GetInt32(8, &credentials);
  1738. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1739. aSavedRequestOut->mValue.credentials() =
  1740. static_cast<RequestCredentials>(credentials);
  1741. int32_t requestContentPolicyType;
  1742. rv = state->GetInt32(9, &requestContentPolicyType);
  1743. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1744. aSavedRequestOut->mValue.contentPolicyType() =
  1745. static_cast<nsContentPolicyType>(requestContentPolicyType);
  1746. int32_t requestCache;
  1747. rv = state->GetInt32(10, &requestCache);
  1748. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1749. aSavedRequestOut->mValue.requestCache() =
  1750. static_cast<RequestCache>(requestCache);
  1751. int32_t requestRedirect;
  1752. rv = state->GetInt32(11, &requestRedirect);
  1753. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1754. aSavedRequestOut->mValue.requestRedirect() =
  1755. static_cast<RequestRedirect>(requestRedirect);
  1756. rv = state->GetString(12, aSavedRequestOut->mValue.integrity());
  1757. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1758. bool nullBody = false;
  1759. rv = state->GetIsNull(13, &nullBody);
  1760. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1761. aSavedRequestOut->mHasBodyId = !nullBody;
  1762. if (aSavedRequestOut->mHasBodyId) {
  1763. rv = ExtractId(state, 13, &aSavedRequestOut->mBodyId);
  1764. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1765. }
  1766. rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  1767. "SELECT "
  1768. "name, "
  1769. "value "
  1770. "FROM request_headers "
  1771. "WHERE entry_id=:entry_id;"
  1772. ), getter_AddRefs(state));
  1773. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1774. rv = state->BindInt32ByName(NS_LITERAL_CSTRING("entry_id"), aEntryId);
  1775. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1776. while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
  1777. HeadersEntry header;
  1778. rv = state->GetUTF8String(0, header.name());
  1779. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1780. rv = state->GetUTF8String(1, header.value());
  1781. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1782. aSavedRequestOut->mValue.headers().AppendElement(header);
  1783. }
  1784. return rv;
  1785. }
  1786. void
  1787. AppendListParamsToQuery(nsACString& aQuery,
  1788. const nsTArray<EntryId>& aEntryIdList,
  1789. uint32_t aPos, int32_t aLen)
  1790. {
  1791. MOZ_ASSERT(!NS_IsMainThread());
  1792. MOZ_DIAGNOSTIC_ASSERT((aPos + aLen) <= aEntryIdList.Length());
  1793. for (int32_t i = aPos; i < aLen; ++i) {
  1794. if (i == 0) {
  1795. aQuery.AppendLiteral("?");
  1796. } else {
  1797. aQuery.AppendLiteral(",?");
  1798. }
  1799. }
  1800. }
  1801. nsresult
  1802. BindListParamsToQuery(mozIStorageStatement* aState,
  1803. const nsTArray<EntryId>& aEntryIdList,
  1804. uint32_t aPos, int32_t aLen)
  1805. {
  1806. MOZ_ASSERT(!NS_IsMainThread());
  1807. MOZ_DIAGNOSTIC_ASSERT((aPos + aLen) <= aEntryIdList.Length());
  1808. for (int32_t i = aPos; i < aLen; ++i) {
  1809. nsresult rv = aState->BindInt32ByIndex(i, aEntryIdList[i]);
  1810. NS_ENSURE_SUCCESS(rv, rv);
  1811. }
  1812. return NS_OK;
  1813. }
  1814. nsresult
  1815. BindId(mozIStorageStatement* aState, const nsACString& aName, const nsID* aId)
  1816. {
  1817. MOZ_ASSERT(!NS_IsMainThread());
  1818. MOZ_DIAGNOSTIC_ASSERT(aState);
  1819. nsresult rv;
  1820. if (!aId) {
  1821. rv = aState->BindNullByName(aName);
  1822. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1823. return rv;
  1824. }
  1825. char idBuf[NSID_LENGTH];
  1826. aId->ToProvidedString(idBuf);
  1827. rv = aState->BindUTF8StringByName(aName, nsDependentCString(idBuf));
  1828. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1829. return rv;
  1830. }
  1831. nsresult
  1832. ExtractId(mozIStorageStatement* aState, uint32_t aPos, nsID* aIdOut)
  1833. {
  1834. MOZ_ASSERT(!NS_IsMainThread());
  1835. MOZ_DIAGNOSTIC_ASSERT(aState);
  1836. MOZ_DIAGNOSTIC_ASSERT(aIdOut);
  1837. nsAutoCString idString;
  1838. nsresult rv = aState->GetUTF8String(aPos, idString);
  1839. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1840. bool success = aIdOut->Parse(idString.get());
  1841. if (NS_WARN_IF(!success)) { return NS_ERROR_UNEXPECTED; }
  1842. return rv;
  1843. }
  1844. nsresult
  1845. CreateAndBindKeyStatement(mozIStorageConnection* aConn,
  1846. const char* aQueryFormat,
  1847. const nsAString& aKey,
  1848. mozIStorageStatement** aStateOut)
  1849. {
  1850. MOZ_DIAGNOSTIC_ASSERT(aConn);
  1851. MOZ_DIAGNOSTIC_ASSERT(aQueryFormat);
  1852. MOZ_DIAGNOSTIC_ASSERT(aStateOut);
  1853. // The key is stored as a blob to avoid encoding issues. An empty string
  1854. // is mapped to NULL for blobs. Normally we would just write the query
  1855. // as "key IS :key" to do the proper NULL checking, but that prevents
  1856. // sqlite from using the key index. Therefore use "IS NULL" explicitly
  1857. // if the key is empty, otherwise use "=:key" so that sqlite uses the
  1858. // index.
  1859. const char* constraint = nullptr;
  1860. if (aKey.IsEmpty()) {
  1861. constraint = "key IS NULL";
  1862. } else {
  1863. constraint = "key=:key";
  1864. }
  1865. nsPrintfCString query(aQueryFormat, constraint);
  1866. nsCOMPtr<mozIStorageStatement> state;
  1867. nsresult rv = aConn->CreateStatement(query, getter_AddRefs(state));
  1868. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1869. if (!aKey.IsEmpty()) {
  1870. rv = state->BindStringAsBlobByName(NS_LITERAL_CSTRING("key"), aKey);
  1871. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1872. }
  1873. state.forget(aStateOut);
  1874. return rv;
  1875. }
  1876. nsresult
  1877. HashCString(nsICryptoHash* aCrypto, const nsACString& aIn, nsACString& aOut)
  1878. {
  1879. MOZ_DIAGNOSTIC_ASSERT(aCrypto);
  1880. nsresult rv = aCrypto->Init(nsICryptoHash::SHA1);
  1881. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1882. rv = aCrypto->Update(reinterpret_cast<const uint8_t*>(aIn.BeginReading()),
  1883. aIn.Length());
  1884. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1885. nsAutoCString fullHash;
  1886. rv = aCrypto->Finish(false /* based64 result */, fullHash);
  1887. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1888. aOut = Substring(fullHash, 0, 8);
  1889. return rv;
  1890. }
  1891. } // namespace
  1892. nsresult
  1893. IncrementalVacuum(mozIStorageConnection* aConn)
  1894. {
  1895. // Determine how much free space is in the database.
  1896. nsCOMPtr<mozIStorageStatement> state;
  1897. nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  1898. "PRAGMA freelist_count;"
  1899. ), getter_AddRefs(state));
  1900. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1901. bool hasMoreData = false;
  1902. rv = state->ExecuteStep(&hasMoreData);
  1903. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1904. int32_t freePages = 0;
  1905. rv = state->GetInt32(0, &freePages);
  1906. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1907. // We have a relatively small page size, so we want to be careful to avoid
  1908. // fragmentation. We already use a growth incremental which will cause
  1909. // sqlite to allocate and release multiple pages at the same time. We can
  1910. // further reduce fragmentation by making our allocated chunks a bit
  1911. // "sticky". This is done by creating some hysteresis where we allocate
  1912. // pages/chunks as soon as we need them, but we only release pages/chunks
  1913. // when we have a large amount of free space. This helps with the case
  1914. // where a page is adding and remove resources causing it to dip back and
  1915. // forth across a chunk boundary.
  1916. //
  1917. // So only proceed with releasing pages if we have more than our constant
  1918. // threshold.
  1919. if (freePages <= kMaxFreePages) {
  1920. return NS_OK;
  1921. }
  1922. // Release the excess pages back to the sqlite VFS. This may also release
  1923. // chunks of multiple pages back to the OS.
  1924. int32_t pagesToRelease = freePages - kMaxFreePages;
  1925. rv = aConn->ExecuteSimpleSQL(nsPrintfCString(
  1926. "PRAGMA incremental_vacuum(%d);", pagesToRelease
  1927. ));
  1928. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1929. // Verify that our incremental vacuum actually did something
  1930. #ifdef DEBUG
  1931. rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  1932. "PRAGMA freelist_count;"
  1933. ), getter_AddRefs(state));
  1934. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1935. hasMoreData = false;
  1936. rv = state->ExecuteStep(&hasMoreData);
  1937. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1938. freePages = 0;
  1939. rv = state->GetInt32(0, &freePages);
  1940. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1941. MOZ_ASSERT(freePages <= kMaxFreePages);
  1942. #endif
  1943. return NS_OK;
  1944. }
  1945. namespace {
  1946. #ifdef DEBUG
  1947. struct Expect
  1948. {
  1949. // Expect exact SQL
  1950. Expect(const char* aName, const char* aType, const char* aSql)
  1951. : mName(aName)
  1952. , mType(aType)
  1953. , mSql(aSql)
  1954. , mIgnoreSql(false)
  1955. { }
  1956. // Ignore SQL
  1957. Expect(const char* aName, const char* aType)
  1958. : mName(aName)
  1959. , mType(aType)
  1960. , mIgnoreSql(true)
  1961. { }
  1962. const nsCString mName;
  1963. const nsCString mType;
  1964. const nsCString mSql;
  1965. const bool mIgnoreSql;
  1966. };
  1967. #endif
  1968. nsresult
  1969. Validate(mozIStorageConnection* aConn)
  1970. {
  1971. int32_t schemaVersion;
  1972. nsresult rv = aConn->GetSchemaVersion(&schemaVersion);
  1973. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  1974. if (NS_WARN_IF(schemaVersion != kLatestSchemaVersion)) {
  1975. return NS_ERROR_FAILURE;
  1976. }
  1977. #ifdef DEBUG
  1978. // This is the schema we expect the database at the latest version to
  1979. // contain. Update this list if you add a new table or index.
  1980. Expect expect[] = {
  1981. Expect("caches", "table", kTableCaches),
  1982. Expect("sqlite_sequence", "table"), // auto-gen by sqlite
  1983. Expect("security_info", "table", kTableSecurityInfo),
  1984. Expect("security_info_hash_index", "index", kIndexSecurityInfoHash),
  1985. Expect("entries", "table", kTableEntries),
  1986. Expect("entries_request_match_index", "index", kIndexEntriesRequest),
  1987. Expect("request_headers", "table", kTableRequestHeaders),
  1988. Expect("response_headers", "table", kTableResponseHeaders),
  1989. Expect("response_headers_name_index", "index", kIndexResponseHeadersName),
  1990. Expect("response_url_list", "table", kTableResponseUrlList),
  1991. Expect("storage", "table", kTableStorage),
  1992. Expect("sqlite_autoindex_storage_1", "index"), // auto-gen by sqlite
  1993. };
  1994. const uint32_t expectLength = sizeof(expect) / sizeof(Expect);
  1995. // Read the schema from the sqlite_master table and compare.
  1996. nsCOMPtr<mozIStorageStatement> state;
  1997. rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  1998. "SELECT name, type, sql FROM sqlite_master;"
  1999. ), getter_AddRefs(state));
  2000. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2001. bool hasMoreData = false;
  2002. while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
  2003. nsAutoCString name;
  2004. rv = state->GetUTF8String(0, name);
  2005. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2006. nsAutoCString type;
  2007. rv = state->GetUTF8String(1, type);
  2008. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2009. nsAutoCString sql;
  2010. rv = state->GetUTF8String(2, sql);
  2011. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2012. bool foundMatch = false;
  2013. for (uint32_t i = 0; i < expectLength; ++i) {
  2014. if (name == expect[i].mName) {
  2015. if (type != expect[i].mType) {
  2016. NS_WARNING(nsPrintfCString("Unexpected type for Cache schema entry %s",
  2017. name.get()).get());
  2018. return NS_ERROR_FAILURE;
  2019. }
  2020. if (!expect[i].mIgnoreSql && sql != expect[i].mSql) {
  2021. NS_WARNING(nsPrintfCString("Unexpected SQL for Cache schema entry %s",
  2022. name.get()).get());
  2023. return NS_ERROR_FAILURE;
  2024. }
  2025. foundMatch = true;
  2026. break;
  2027. }
  2028. }
  2029. if (NS_WARN_IF(!foundMatch)) {
  2030. NS_WARNING(nsPrintfCString("Unexpected schema entry %s in Cache database",
  2031. name.get()).get());
  2032. return NS_ERROR_FAILURE;
  2033. }
  2034. }
  2035. #endif
  2036. return rv;
  2037. }
  2038. // -----
  2039. // Schema migration code
  2040. // -----
  2041. typedef nsresult (*MigrationFunc)(mozIStorageConnection*, bool&);
  2042. struct Migration
  2043. {
  2044. Migration(int32_t aFromVersion, MigrationFunc aFunc)
  2045. : mFromVersion(aFromVersion)
  2046. , mFunc(aFunc)
  2047. { }
  2048. int32_t mFromVersion;
  2049. MigrationFunc mFunc;
  2050. };
  2051. // Declare migration functions here. Each function should upgrade
  2052. // the version by a single increment. Don't skip versions.
  2053. nsresult MigrateFrom15To16(mozIStorageConnection* aConn, bool& aRewriteSchema);
  2054. nsresult MigrateFrom16To17(mozIStorageConnection* aConn, bool& aRewriteSchema);
  2055. nsresult MigrateFrom17To18(mozIStorageConnection* aConn, bool& aRewriteSchema);
  2056. nsresult MigrateFrom18To19(mozIStorageConnection* aConn, bool& aRewriteSchema);
  2057. nsresult MigrateFrom19To20(mozIStorageConnection* aConn, bool& aRewriteSchema);
  2058. nsresult MigrateFrom20To21(mozIStorageConnection* aConn, bool& aRewriteSchema);
  2059. nsresult MigrateFrom21To22(mozIStorageConnection* aConn, bool& aRewriteSchema);
  2060. nsresult MigrateFrom22To23(mozIStorageConnection* aConn, bool& aRewriteSchema);
  2061. nsresult MigrateFrom23To24(mozIStorageConnection* aConn, bool& aRewriteSchema);
  2062. // Configure migration functions to run for the given starting version.
  2063. Migration sMigrationList[] = {
  2064. Migration(15, MigrateFrom15To16),
  2065. Migration(16, MigrateFrom16To17),
  2066. Migration(17, MigrateFrom17To18),
  2067. Migration(18, MigrateFrom18To19),
  2068. Migration(19, MigrateFrom19To20),
  2069. Migration(20, MigrateFrom20To21),
  2070. Migration(21, MigrateFrom21To22),
  2071. Migration(22, MigrateFrom22To23),
  2072. Migration(23, MigrateFrom23To24),
  2073. };
  2074. uint32_t sMigrationListLength = sizeof(sMigrationList) / sizeof(Migration);
  2075. nsresult
  2076. RewriteEntriesSchema(mozIStorageConnection* aConn)
  2077. {
  2078. nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  2079. "PRAGMA writable_schema = ON"
  2080. ));
  2081. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2082. nsCOMPtr<mozIStorageStatement> state;
  2083. rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  2084. "UPDATE sqlite_master SET sql=:sql WHERE name='entries'"
  2085. ), getter_AddRefs(state));
  2086. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2087. rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("sql"),
  2088. nsDependentCString(kTableEntries));
  2089. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2090. rv = state->Execute();
  2091. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2092. rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  2093. "PRAGMA writable_schema = OFF"
  2094. ));
  2095. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2096. return rv;
  2097. }
  2098. nsresult
  2099. Migrate(mozIStorageConnection* aConn)
  2100. {
  2101. MOZ_ASSERT(!NS_IsMainThread());
  2102. MOZ_DIAGNOSTIC_ASSERT(aConn);
  2103. int32_t currentVersion = 0;
  2104. nsresult rv = aConn->GetSchemaVersion(&currentVersion);
  2105. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2106. bool rewriteSchema = false;
  2107. while (currentVersion < kLatestSchemaVersion) {
  2108. // Wiping old databases is handled in DBAction because it requires
  2109. // making a whole new mozIStorageConnection. Make sure we don't
  2110. // accidentally get here for one of those old databases.
  2111. MOZ_DIAGNOSTIC_ASSERT(currentVersion >= kFirstShippedSchemaVersion);
  2112. for (uint32_t i = 0; i < sMigrationListLength; ++i) {
  2113. if (sMigrationList[i].mFromVersion == currentVersion) {
  2114. bool shouldRewrite = false;
  2115. rv = sMigrationList[i].mFunc(aConn, shouldRewrite);
  2116. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2117. if (shouldRewrite) {
  2118. rewriteSchema = true;
  2119. }
  2120. break;
  2121. }
  2122. }
  2123. #if defined(DEBUG) || !defined(RELEASE_OR_BETA)
  2124. int32_t lastVersion = currentVersion;
  2125. #endif
  2126. rv = aConn->GetSchemaVersion(&currentVersion);
  2127. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2128. MOZ_DIAGNOSTIC_ASSERT(currentVersion > lastVersion);
  2129. }
  2130. MOZ_DIAGNOSTIC_ASSERT(currentVersion == kLatestSchemaVersion);
  2131. if (rewriteSchema) {
  2132. // Now overwrite the master SQL for the entries table to remove the column
  2133. // default value. This is also necessary for our Validate() method to
  2134. // pass on this database.
  2135. rv = RewriteEntriesSchema(aConn);
  2136. }
  2137. return rv;
  2138. }
  2139. nsresult MigrateFrom15To16(mozIStorageConnection* aConn, bool& aRewriteSchema)
  2140. {
  2141. MOZ_ASSERT(!NS_IsMainThread());
  2142. MOZ_DIAGNOSTIC_ASSERT(aConn);
  2143. // Add the request_redirect column with a default value of "follow". Note,
  2144. // we only use a default value here because its required by ALTER TABLE and
  2145. // we need to apply the default "follow" to existing records in the table.
  2146. // We don't actually want to keep the default in the schema for future
  2147. // INSERTs.
  2148. nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  2149. "ALTER TABLE entries "
  2150. "ADD COLUMN request_redirect INTEGER NOT NULL DEFAULT 0"
  2151. ));
  2152. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2153. rv = aConn->SetSchemaVersion(16);
  2154. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2155. aRewriteSchema = true;
  2156. return rv;
  2157. }
  2158. nsresult
  2159. MigrateFrom16To17(mozIStorageConnection* aConn, bool& aRewriteSchema)
  2160. {
  2161. MOZ_ASSERT(!NS_IsMainThread());
  2162. MOZ_DIAGNOSTIC_ASSERT(aConn);
  2163. // This migration path removes the response_redirected and
  2164. // response_redirected_url columns from the entries table. sqlite doesn't
  2165. // support removing a column from a table using ALTER TABLE, so we need to
  2166. // create a new table without those columns, fill it up with the existing
  2167. // data, and then drop the original table and rename the new one to the old
  2168. // one.
  2169. // Create a new_entries table with the new fields as of version 17.
  2170. nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  2171. "CREATE TABLE new_entries ("
  2172. "id INTEGER NOT NULL PRIMARY KEY, "
  2173. "request_method TEXT NOT NULL, "
  2174. "request_url_no_query TEXT NOT NULL, "
  2175. "request_url_no_query_hash BLOB NOT NULL, "
  2176. "request_url_query TEXT NOT NULL, "
  2177. "request_url_query_hash BLOB NOT NULL, "
  2178. "request_referrer TEXT NOT NULL, "
  2179. "request_headers_guard INTEGER NOT NULL, "
  2180. "request_mode INTEGER NOT NULL, "
  2181. "request_credentials INTEGER NOT NULL, "
  2182. "request_contentpolicytype INTEGER NOT NULL, "
  2183. "request_cache INTEGER NOT NULL, "
  2184. "request_body_id TEXT NULL, "
  2185. "response_type INTEGER NOT NULL, "
  2186. "response_url TEXT NOT NULL, "
  2187. "response_status INTEGER NOT NULL, "
  2188. "response_status_text TEXT NOT NULL, "
  2189. "response_headers_guard INTEGER NOT NULL, "
  2190. "response_body_id TEXT NULL, "
  2191. "response_security_info_id INTEGER NULL REFERENCES security_info(id), "
  2192. "response_principal_info TEXT NOT NULL, "
  2193. "cache_id INTEGER NOT NULL REFERENCES caches(id) ON DELETE CASCADE, "
  2194. "request_redirect INTEGER NOT NULL"
  2195. ")"
  2196. ));
  2197. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2198. // Copy all of the data to the newly created table.
  2199. rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  2200. "INSERT INTO new_entries ("
  2201. "id, "
  2202. "request_method, "
  2203. "request_url_no_query, "
  2204. "request_url_no_query_hash, "
  2205. "request_url_query, "
  2206. "request_url_query_hash, "
  2207. "request_referrer, "
  2208. "request_headers_guard, "
  2209. "request_mode, "
  2210. "request_credentials, "
  2211. "request_contentpolicytype, "
  2212. "request_cache, "
  2213. "request_redirect, "
  2214. "request_body_id, "
  2215. "response_type, "
  2216. "response_url, "
  2217. "response_status, "
  2218. "response_status_text, "
  2219. "response_headers_guard, "
  2220. "response_body_id, "
  2221. "response_security_info_id, "
  2222. "response_principal_info, "
  2223. "cache_id "
  2224. ") SELECT "
  2225. "id, "
  2226. "request_method, "
  2227. "request_url_no_query, "
  2228. "request_url_no_query_hash, "
  2229. "request_url_query, "
  2230. "request_url_query_hash, "
  2231. "request_referrer, "
  2232. "request_headers_guard, "
  2233. "request_mode, "
  2234. "request_credentials, "
  2235. "request_contentpolicytype, "
  2236. "request_cache, "
  2237. "request_redirect, "
  2238. "request_body_id, "
  2239. "response_type, "
  2240. "response_url, "
  2241. "response_status, "
  2242. "response_status_text, "
  2243. "response_headers_guard, "
  2244. "response_body_id, "
  2245. "response_security_info_id, "
  2246. "response_principal_info, "
  2247. "cache_id "
  2248. "FROM entries;"
  2249. ));
  2250. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2251. // Remove the old table.
  2252. rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  2253. "DROP TABLE entries;"
  2254. ));
  2255. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2256. // Rename new_entries to entries.
  2257. rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  2258. "ALTER TABLE new_entries RENAME to entries;"
  2259. ));
  2260. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2261. // Now, recreate our indices.
  2262. rv = aConn->ExecuteSimpleSQL(nsDependentCString(kIndexEntriesRequest));
  2263. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2264. // Revalidate the foreign key constraints, and ensure that there are no
  2265. // violations.
  2266. nsCOMPtr<mozIStorageStatement> state;
  2267. rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  2268. "PRAGMA foreign_key_check;"
  2269. ), getter_AddRefs(state));
  2270. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2271. bool hasMoreData = false;
  2272. rv = state->ExecuteStep(&hasMoreData);
  2273. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2274. if (NS_WARN_IF(hasMoreData)) { return NS_ERROR_FAILURE; }
  2275. rv = aConn->SetSchemaVersion(17);
  2276. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2277. return rv;
  2278. }
  2279. nsresult
  2280. MigrateFrom17To18(mozIStorageConnection* aConn, bool& aRewriteSchema)
  2281. {
  2282. MOZ_ASSERT(!NS_IsMainThread());
  2283. MOZ_DIAGNOSTIC_ASSERT(aConn);
  2284. // This migration is needed in order to remove "only-if-cached" RequestCache
  2285. // values from the database. This enum value was removed from the spec in
  2286. // https://github.com/whatwg/fetch/issues/39 but we unfortunately happily
  2287. // accepted this value in the Request constructor.
  2288. //
  2289. // There is no good value to upgrade this to, so we just stick to "default".
  2290. static_assert(int(RequestCache::Default) == 0,
  2291. "This is where the 0 below comes from!");
  2292. nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  2293. "UPDATE entries SET request_cache = 0 "
  2294. "WHERE request_cache = 5;"
  2295. ));
  2296. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2297. rv = aConn->SetSchemaVersion(18);
  2298. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2299. return rv;
  2300. }
  2301. nsresult
  2302. MigrateFrom18To19(mozIStorageConnection* aConn, bool& aRewriteSchema)
  2303. {
  2304. MOZ_ASSERT(!NS_IsMainThread());
  2305. MOZ_DIAGNOSTIC_ASSERT(aConn);
  2306. // This migration is needed in order to update the RequestMode values for
  2307. // Request objects corresponding to a navigation content policy type to
  2308. // "navigate".
  2309. static_assert(int(nsIContentPolicy::TYPE_DOCUMENT) == 6 &&
  2310. int(nsIContentPolicy::TYPE_SUBDOCUMENT) == 7 &&
  2311. int(nsIContentPolicy::TYPE_INTERNAL_FRAME) == 28 &&
  2312. int(nsIContentPolicy::TYPE_INTERNAL_IFRAME) == 29 &&
  2313. int(nsIContentPolicy::TYPE_REFRESH) == 8 &&
  2314. int(RequestMode::Navigate) == 3,
  2315. "This is where the numbers below come from!");
  2316. nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  2317. "UPDATE entries SET request_mode = 3 "
  2318. "WHERE request_contentpolicytype IN (6, 7, 28, 29, 8);"
  2319. ));
  2320. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2321. rv = aConn->SetSchemaVersion(19);
  2322. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2323. return rv;
  2324. }
  2325. nsresult MigrateFrom19To20(mozIStorageConnection* aConn, bool& aRewriteSchema)
  2326. {
  2327. MOZ_ASSERT(!NS_IsMainThread());
  2328. MOZ_DIAGNOSTIC_ASSERT(aConn);
  2329. // Add the request_referrer_policy column with a default value of
  2330. // "no-referrer-when-downgrade". Note, we only use a default value here
  2331. // because its required by ALTER TABLE and we need to apply the default
  2332. // "no-referrer-when-downgrade" to existing records in the table. We don't
  2333. // actually want to keep the default in the schema for future INSERTs.
  2334. nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  2335. "ALTER TABLE entries "
  2336. "ADD COLUMN request_referrer_policy INTEGER NOT NULL DEFAULT 2"
  2337. ));
  2338. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2339. rv = aConn->SetSchemaVersion(20);
  2340. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2341. aRewriteSchema = true;
  2342. return rv;
  2343. }
  2344. nsresult MigrateFrom20To21(mozIStorageConnection* aConn, bool& aRewriteSchema)
  2345. {
  2346. MOZ_ASSERT(!NS_IsMainThread());
  2347. MOZ_DIAGNOSTIC_ASSERT(aConn);
  2348. // This migration creates response_url_list table to store response_url and
  2349. // removes the response_url column from the entries table.
  2350. // sqlite doesn't support removing a column from a table using ALTER TABLE,
  2351. // so we need to create a new table without those columns, fill it up with the
  2352. // existing data, and then drop the original table and rename the new one to
  2353. // the old one.
  2354. // Create a new_entries table with the new fields as of version 21.
  2355. nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  2356. "CREATE TABLE new_entries ("
  2357. "id INTEGER NOT NULL PRIMARY KEY, "
  2358. "request_method TEXT NOT NULL, "
  2359. "request_url_no_query TEXT NOT NULL, "
  2360. "request_url_no_query_hash BLOB NOT NULL, "
  2361. "request_url_query TEXT NOT NULL, "
  2362. "request_url_query_hash BLOB NOT NULL, "
  2363. "request_referrer TEXT NOT NULL, "
  2364. "request_headers_guard INTEGER NOT NULL, "
  2365. "request_mode INTEGER NOT NULL, "
  2366. "request_credentials INTEGER NOT NULL, "
  2367. "request_contentpolicytype INTEGER NOT NULL, "
  2368. "request_cache INTEGER NOT NULL, "
  2369. "request_body_id TEXT NULL, "
  2370. "response_type INTEGER NOT NULL, "
  2371. "response_status INTEGER NOT NULL, "
  2372. "response_status_text TEXT NOT NULL, "
  2373. "response_headers_guard INTEGER NOT NULL, "
  2374. "response_body_id TEXT NULL, "
  2375. "response_security_info_id INTEGER NULL REFERENCES security_info(id), "
  2376. "response_principal_info TEXT NOT NULL, "
  2377. "cache_id INTEGER NOT NULL REFERENCES caches(id) ON DELETE CASCADE, "
  2378. "request_redirect INTEGER NOT NULL, "
  2379. "request_referrer_policy INTEGER NOT NULL"
  2380. ")"
  2381. ));
  2382. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2383. // Create a response_url_list table with the new fields as of version 21.
  2384. rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  2385. "CREATE TABLE response_url_list ("
  2386. "url TEXT NOT NULL, "
  2387. "entry_id INTEGER NOT NULL REFERENCES entries(id) ON DELETE CASCADE"
  2388. ")"
  2389. ));
  2390. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2391. // Copy all of the data to the newly created entries table.
  2392. rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  2393. "INSERT INTO new_entries ("
  2394. "id, "
  2395. "request_method, "
  2396. "request_url_no_query, "
  2397. "request_url_no_query_hash, "
  2398. "request_url_query, "
  2399. "request_url_query_hash, "
  2400. "request_referrer, "
  2401. "request_headers_guard, "
  2402. "request_mode, "
  2403. "request_credentials, "
  2404. "request_contentpolicytype, "
  2405. "request_cache, "
  2406. "request_redirect, "
  2407. "request_referrer_policy, "
  2408. "request_body_id, "
  2409. "response_type, "
  2410. "response_status, "
  2411. "response_status_text, "
  2412. "response_headers_guard, "
  2413. "response_body_id, "
  2414. "response_security_info_id, "
  2415. "response_principal_info, "
  2416. "cache_id "
  2417. ") SELECT "
  2418. "id, "
  2419. "request_method, "
  2420. "request_url_no_query, "
  2421. "request_url_no_query_hash, "
  2422. "request_url_query, "
  2423. "request_url_query_hash, "
  2424. "request_referrer, "
  2425. "request_headers_guard, "
  2426. "request_mode, "
  2427. "request_credentials, "
  2428. "request_contentpolicytype, "
  2429. "request_cache, "
  2430. "request_redirect, "
  2431. "request_referrer_policy, "
  2432. "request_body_id, "
  2433. "response_type, "
  2434. "response_status, "
  2435. "response_status_text, "
  2436. "response_headers_guard, "
  2437. "response_body_id, "
  2438. "response_security_info_id, "
  2439. "response_principal_info, "
  2440. "cache_id "
  2441. "FROM entries;"
  2442. ));
  2443. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2444. // Copy reponse_url to the newly created response_url_list table.
  2445. rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  2446. "INSERT INTO response_url_list ("
  2447. "url, "
  2448. "entry_id "
  2449. ") SELECT "
  2450. "response_url, "
  2451. "id "
  2452. "FROM entries;"
  2453. ));
  2454. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2455. // Remove the old table.
  2456. rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  2457. "DROP TABLE entries;"
  2458. ));
  2459. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2460. // Rename new_entries to entries.
  2461. rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  2462. "ALTER TABLE new_entries RENAME to entries;"
  2463. ));
  2464. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2465. // Now, recreate our indices.
  2466. rv = aConn->ExecuteSimpleSQL(nsDependentCString(kIndexEntriesRequest));
  2467. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2468. // Revalidate the foreign key constraints, and ensure that there are no
  2469. // violations.
  2470. nsCOMPtr<mozIStorageStatement> state;
  2471. rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
  2472. "PRAGMA foreign_key_check;"
  2473. ), getter_AddRefs(state));
  2474. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2475. bool hasMoreData = false;
  2476. rv = state->ExecuteStep(&hasMoreData);
  2477. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2478. if (NS_WARN_IF(hasMoreData)) { return NS_ERROR_FAILURE; }
  2479. rv = aConn->SetSchemaVersion(21);
  2480. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2481. aRewriteSchema = true;
  2482. return rv;
  2483. }
  2484. nsresult MigrateFrom21To22(mozIStorageConnection* aConn, bool& aRewriteSchema)
  2485. {
  2486. MOZ_ASSERT(!NS_IsMainThread());
  2487. MOZ_DIAGNOSTIC_ASSERT(aConn);
  2488. // Add the request_integrity column.
  2489. nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  2490. "ALTER TABLE entries "
  2491. "ADD COLUMN request_integrity TEXT NULL"
  2492. ));
  2493. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2494. rv = aConn->SetSchemaVersion(22);
  2495. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2496. aRewriteSchema = true;
  2497. return rv;
  2498. }
  2499. nsresult MigrateFrom22To23(mozIStorageConnection* aConn, bool& aRewriteSchema)
  2500. {
  2501. MOZ_ASSERT(!NS_IsMainThread());
  2502. MOZ_DIAGNOSTIC_ASSERT(aConn);
  2503. // The only change between 22 and 23 was a different snappy compression
  2504. // format, but it's backwards-compatible.
  2505. nsresult rv = aConn->SetSchemaVersion(23);
  2506. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2507. return rv;
  2508. }
  2509. nsresult MigrateFrom23To24(mozIStorageConnection* aConn, bool& aRewriteSchema)
  2510. {
  2511. MOZ_ASSERT(!NS_IsMainThread());
  2512. MOZ_DIAGNOSTIC_ASSERT(aConn);
  2513. // Add the request_url_fragment column.
  2514. nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  2515. "ALTER TABLE entries "
  2516. "ADD COLUMN request_url_fragment TEXT NOT NULL DEFAULT ''"
  2517. ));
  2518. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2519. rv = aConn->SetSchemaVersion(24);
  2520. if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
  2521. aRewriteSchema = true;
  2522. return rv;
  2523. }
  2524. } // anonymous namespace
  2525. } // namespace db
  2526. } // namespace cache
  2527. } // namespace dom
  2528. } // namespace mozilla