зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1143959 - Set the journal mode and foreign key pragmas for all DBActions; r=bkelly
Before this patch, we would only set these pragmas as part of CreateSchema which runs in SetupAction. This meant that the connection used to perform other DBActions would not have had these pragmas applied. As a result, sqlite would not honor foreign keys on such connections, so the cascade delete rules responsible for deleting rows from request_headers and response_headers would not get executed when DBSchema::CachePut deleted the old entry before adding a new one. The test in the patch demonstrates how this could result in an observable breakage. Before this patch, the response headers stored in the cache for the overwritten entry would reflect both `Mirrored: `foo' and `Mirrored: bar' headers, which means that attempting to get this header on the cached response would return the first entry, `foo'.
This commit is contained in:
Родитель
e250f1ab8f
Коммит
eaf970dc6a
|
@ -154,6 +154,9 @@ DBAction::OpenConnection(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
|
|||
rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(conn));
|
||||
}
|
||||
|
||||
rv = DBSchema::InitializeConnection(conn);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
conn.forget(aConnOut);
|
||||
|
||||
return rv;
|
||||
|
|
|
@ -34,21 +34,9 @@ DBSchema::CreateSchema(mozIStorageConnection* aConn)
|
|||
MOZ_ASSERT(aConn);
|
||||
|
||||
nsAutoCString pragmas(
|
||||
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
|
||||
// Switch the journaling mode to TRUNCATE to avoid changing the directory
|
||||
// structure at the conclusion of every transaction for devices with slower
|
||||
// file systems.
|
||||
"PRAGMA journal_mode = TRUNCATE; "
|
||||
#endif
|
||||
"PRAGMA foreign_keys = ON; "
|
||||
// Enable auto-vaccum but in incremental mode in order to avoid doing a lot
|
||||
// of work at the end of each transaction.
|
||||
"PRAGMA auto_vacuum = INCREMENTAL; "
|
||||
|
||||
// Note, the default encoding of UTF-8 is preferred. mozStorage does all
|
||||
// the work necessary to convert UTF-16 nsString values for us. We don't
|
||||
// need ordering and the binary equality operations are correct. So, do
|
||||
// NOT set PRAGMA encoding to UTF-16.
|
||||
);
|
||||
|
||||
nsresult rv = aConn->ExecuteSimpleSQL(pragmas);
|
||||
|
@ -175,6 +163,37 @@ DBSchema::CreateSchema(mozIStorageConnection* aConn)
|
|||
return rv;
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
DBSchema::InitializeConnection(mozIStorageConnection* aConn)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(aConn);
|
||||
|
||||
// This function needs to perform per-connection initialization tasks that
|
||||
// need to happen regardless of the schema.
|
||||
|
||||
nsAutoCString pragmas(
|
||||
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
|
||||
// Switch the journaling mode to TRUNCATE to avoid changing the directory
|
||||
// structure at the conclusion of every transaction for devices with slower
|
||||
// file systems.
|
||||
"PRAGMA journal_mode = TRUNCATE; "
|
||||
#endif
|
||||
"PRAGMA foreign_keys = ON; "
|
||||
|
||||
// Note, the default encoding of UTF-8 is preferred. mozStorage does all
|
||||
// the work necessary to convert UTF-16 nsString values for us. We don't
|
||||
// need ordering and the binary equality operations are correct. So, do
|
||||
// NOT set PRAGMA encoding to UTF-16.
|
||||
);
|
||||
|
||||
nsresult rv = aConn->ExecuteSimpleSQL(pragmas);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
DBSchema::CreateCache(mozIStorageConnection* aConn, CacheId* aCacheIdOut)
|
||||
|
|
|
@ -33,6 +33,7 @@ class DBSchema MOZ_FINAL
|
|||
{
|
||||
public:
|
||||
static nsresult CreateSchema(mozIStorageConnection* aConn);
|
||||
static nsresult InitializeConnection(mozIStorageConnection* aConn);
|
||||
|
||||
static nsresult CreateCache(mozIStorageConnection* aConn,
|
||||
CacheId* aCacheIdOut);
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
function handleRequest(request, response) {
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setHeader("Mirrored", request.getHeader("Mirror"));
|
||||
response.write(request.getHeader("Mirror"));
|
||||
}
|
|
@ -10,8 +10,11 @@ support-files =
|
|||
serviceworker_driver.js
|
||||
test_cache_match_request.js
|
||||
test_cache_matchAll_request.js
|
||||
test_cache_overwrite.js
|
||||
mirror.sjs
|
||||
|
||||
[test_cache.html]
|
||||
[test_cache_add.html]
|
||||
[test_cache_match_request.html]
|
||||
[test_cache_matchAll_request.html]
|
||||
[test_cache_overwrite.html]
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test what happens when you overwrite a cache entry</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="text/javascript" src="driver.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<iframe id="frame"></iframe>
|
||||
<script class="testbody" type="text/javascript">
|
||||
runTests("test_cache_overwrite.js")
|
||||
.then(function() {
|
||||
SimpleTest.finish();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,44 @@
|
|||
var requestURL = "//mochi.test:8888/tests/dom/cache/test/mochitest/mirror.sjs?" + context;
|
||||
var response;
|
||||
var c;
|
||||
var responseText;
|
||||
var name = "match-mirror" + context;
|
||||
|
||||
function checkResponse(r) {
|
||||
ok(r !== response, "The objects should not be the same");
|
||||
is(r.url, response.url.replace("#fragment", ""),
|
||||
"The URLs should be the same");
|
||||
is(r.status, response.status, "The status codes should be the same");
|
||||
is(r.type, response.type, "The response types should be the same");
|
||||
is(r.ok, response.ok, "Both responses should have succeeded");
|
||||
is(r.statusText, response.statusText,
|
||||
"Both responses should have the same status text");
|
||||
is(r.headers.get("Mirrored"), response.headers.get("Mirrored"),
|
||||
"Both responses should have the same Mirrored header");
|
||||
return r.text().then(function(text) {
|
||||
is(text, responseText, "The response body should be correct");
|
||||
});
|
||||
}
|
||||
|
||||
fetch(new Request(requestURL, {headers: {"Mirror": "bar"}})).then(function(r) {
|
||||
is(r.headers.get("Mirrored"), "bar", "The server should give back the correct header");
|
||||
response = r;
|
||||
return response.text();
|
||||
}).then(function(text) {
|
||||
responseText = text;
|
||||
return caches.open(name);
|
||||
}).then(function(cache) {
|
||||
c = cache;
|
||||
return c.add(new Request(requestURL, {headers: {"Mirror": "foo"}}));
|
||||
}).then(function() {
|
||||
// Overwrite the request, to replace the entry stored in response_headers
|
||||
// with a different value.
|
||||
return c.add(new Request(requestURL, {headers: {"Mirror": "bar"}}));
|
||||
}).then(function() {
|
||||
return c.matchAll();
|
||||
}).then(function(r) {
|
||||
is(r.length, 1, "Only one request should be in the cache");
|
||||
return checkResponse(r[0]);
|
||||
}).then(function() {
|
||||
testDone();
|
||||
});
|
Загрузка…
Ссылка в новой задаче