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