--- a/extensions/cookie/test/unit/test_permmanager_migrate_4-7.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_4-7.js
@@ -8,20 +8,16 @@ var PERMISSIONS_FILE_NAME = "permissions
function GetPermissionsFile(profile)
{
let file = profile.clone();
file.append(PERMISSIONS_FILE_NAME);
return file;
}
-function run_test() {
- run_next_test();
-}
-
add_task(function* test() {
/* Create and set up the permissions database */
let profile = do_get_profile();
let db = Services.storage.openDatabase(GetPermissionsFile(profile));
db.schemaVersion = 4;
db.executeSimpleSQL(
@@ -54,17 +50,21 @@ add_task(function* test() {
stmtInsert.bindByName("type", type);
stmtInsert.bindByName("permission", permission);
stmtInsert.bindByName("expireType", expireType);
stmtInsert.bindByName("expireTime", expireTime);
stmtInsert.bindByName("modificationTime", modificationTime);
stmtInsert.bindByName("appId", appId);
stmtInsert.bindByName("isInBrowserElement", isInBrowserElement);
- stmtInsert.execute();
+ try {
+ stmtInsert.execute();
+ } finally {
+ stmtInsert.reset();
+ }
return {
id: thisId,
host: host,
type: type,
permission: permission,
expireType: expireType,
expireTime: expireTime,
@@ -194,14 +194,18 @@ add_task(function* test() {
let db = Services.storage.openDatabase(GetPermissionsFile(profile));
do_check_true(db.tableExists("moz_perms"));
do_check_true(db.tableExists("moz_hosts"));
do_check_false(db.tableExists("moz_hosts_is_backup"));
do_check_false(db.tableExists("moz_perms_v6"));
// The moz_hosts table should still exist but be empty
let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts");
- mozHostsCount.executeStep();
- do_check_eq(mozHostsCount.getInt64(0), 0);
+ try {
+ mozHostsCount.executeStep();
+ do_check_eq(mozHostsCount.getInt64(0), 0);
+ } finally {
+ mozHostsCount.finalize();
+ }
db.close();
}
});
--- a/extensions/cookie/test/unit/test_permmanager_migrate_4-7_no_history.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_4-7_no_history.js
@@ -37,20 +37,16 @@ function GetPermissionsFile(profile)
file.append(PERMISSIONS_FILE_NAME);
return file;
}
/*
* Done nsINavHistoryService code
*/
-function run_test() {
- run_next_test();
-}
-
add_task(function test() {
/* Create and set up the permissions database */
let profile = do_get_profile();
// Make sure that we can't resolve the nsINavHistoryService
try {
Cc['@mozilla.org/browser/nav-history-service;1'].getService(Ci.nsINavHistoryService);
do_check_true(false, "There shouldn't have been a nsINavHistoryService");
@@ -91,17 +87,21 @@ add_task(function test() {
stmtInsert.bindByName("type", type);
stmtInsert.bindByName("permission", permission);
stmtInsert.bindByName("expireType", expireType);
stmtInsert.bindByName("expireTime", expireTime);
stmtInsert.bindByName("modificationTime", modificationTime);
stmtInsert.bindByName("appId", appId);
stmtInsert.bindByName("isInBrowserElement", isInBrowserElement);
- stmtInsert.execute();
+ try {
+ stmtInsert.execute();
+ } finally {
+ stmtInsert.reset();
+ }
return {
id: thisId,
host: host,
type: type,
permission: permission,
expireType: expireType,
expireTime: expireTime,
@@ -211,16 +211,20 @@ add_task(function test() {
let db = Services.storage.openDatabase(GetPermissionsFile(profile));
do_check_true(db.tableExists("moz_perms"));
do_check_true(db.tableExists("moz_hosts"));
do_check_false(db.tableExists("moz_hosts_is_backup"));
do_check_false(db.tableExists("moz_perms_v6"));
// The moz_hosts table should still exist but be empty
let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts");
- mozHostsCount.executeStep();
- do_check_eq(mozHostsCount.getInt64(0), 0);
+ try {
+ mozHostsCount.executeStep();
+ do_check_eq(mozHostsCount.getInt64(0), 0);
+ } finally {
+ mozHostsCount.finalize();
+ }
db.close();
}
cleanupFactory();
});
--- a/extensions/cookie/test/unit/test_permmanager_migrate_5-7a.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_5-7a.js
@@ -8,20 +8,16 @@ var PERMISSIONS_FILE_NAME = "permissions
function GetPermissionsFile(profile)
{
let file = profile.clone();
file.append(PERMISSIONS_FILE_NAME);
return file;
}
-function run_test() {
- run_next_test();
-}
-
add_task(function* test() {
/* Create and set up the permissions database */
let profile = do_get_profile();
let db = Services.storage.openDatabase(GetPermissionsFile(profile));
db.schemaVersion = 5;
/*
@@ -76,17 +72,21 @@ add_task(function* test() {
stmt5Insert.bindByName("id", thisId);
stmt5Insert.bindByName("origin", origin);
stmt5Insert.bindByName("type", type);
stmt5Insert.bindByName("permission", permission);
stmt5Insert.bindByName("expireType", expireType);
stmt5Insert.bindByName("expireTime", expireTime);
stmt5Insert.bindByName("modificationTime", modificationTime);
- stmt5Insert.execute();
+ try {
+ stmt5Insert.execute();
+ } finally {
+ stmt5Insert.reset();
+ }
return {
id: thisId,
origin: origin,
type: type,
permission: permission,
expireType: expireType,
expireTime: expireTime,
@@ -102,17 +102,21 @@ add_task(function* test() {
stmtInsert.bindByName("type", type);
stmtInsert.bindByName("permission", permission);
stmtInsert.bindByName("expireType", expireType);
stmtInsert.bindByName("expireTime", expireTime);
stmtInsert.bindByName("modificationTime", modificationTime);
stmtInsert.bindByName("appId", appId);
stmtInsert.bindByName("isInBrowserElement", isInBrowserElement);
- stmtInsert.execute();
+ try {
+ stmtInsert.execute();
+ } finally {
+ stmtInsert.reset();
+ }
return {
id: thisId,
host: host,
type: type,
permission: permission,
expireType: expireType,
expireTime: expireTime,
@@ -146,16 +150,17 @@ add_task(function* test() {
insertHost("file:///another/file.html", "A", 1, 0, 0, 0, 0, false),
insertHost("moz-nullprincipal:{8695105a-adbe-4e4e-8083-851faa5ca2d7}", "A", 1, 0, 0, 0, 0, false),
insertHost("moz-nullprincipal:{12ahjksd-akjs-asd3-8393-asdu2189asdu}", "B", 1, 0, 0, 0, 0, false),
insertHost("<file>", "A", 1, 0, 0, 0, 0, false),
insertHost("<file>", "B", 1, 0, 0, 0, 0, false),
];
// CLose the db connection
+ stmt5Insert.finalize();
stmtInsert.finalize();
db.close();
stmtInsert = null;
db = null;
let expected = [
// The http:// entries under foo.com won't be inserted, as there are history entries for foo.com,
// and http://foo.com or a subdomain are never visited.
@@ -248,37 +253,48 @@ add_task(function* test() {
let db = Services.storage.openDatabase(GetPermissionsFile(profile));
do_check_true(db.tableExists("moz_perms"));
do_check_true(db.tableExists("moz_hosts"));
do_check_false(db.tableExists("moz_hosts_is_backup"));
do_check_true(db.tableExists("moz_perms_v6"));
// The moz_hosts table should still exist but be empty
let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts");
- mozHostsCount.executeStep();
- do_check_eq(mozHostsCount.getInt64(0), 0);
+ try {
+ mozHostsCount.executeStep();
+ do_check_eq(mozHostsCount.getInt64(0), 0);
+ } finally {
+ mozHostsCount.finalize();
+ }
// Check that the moz_perms_v6 table contains the backup of the entry we created
let mozPermsV6Stmt = db.createStatement("SELECT " +
"origin, type, permission, expireType, expireTime, modificationTime " +
"FROM moz_perms_v6 WHERE id = :id");
-
- // Check that the moz_hosts table still contains the correct values.
- created5.forEach((it) => {
- mozPermsV6Stmt.reset();
- mozPermsV6Stmt.bindByName("id", it.id);
- mozPermsV6Stmt.executeStep();
- do_check_eq(mozPermsV6Stmt.getUTF8String(0), it.origin);
- do_check_eq(mozPermsV6Stmt.getUTF8String(1), it.type);
- do_check_eq(mozPermsV6Stmt.getInt64(2), it.permission);
- do_check_eq(mozPermsV6Stmt.getInt64(3), it.expireType);
- do_check_eq(mozPermsV6Stmt.getInt64(4), it.expireTime);
- do_check_eq(mozPermsV6Stmt.getInt64(5), it.modificationTime);
- });
+ try {
+ // Check that the moz_hosts table still contains the correct values.
+ created5.forEach((it) => {
+ mozPermsV6Stmt.reset();
+ mozPermsV6Stmt.bindByName("id", it.id);
+ mozPermsV6Stmt.executeStep();
+ do_check_eq(mozPermsV6Stmt.getUTF8String(0), it.origin);
+ do_check_eq(mozPermsV6Stmt.getUTF8String(1), it.type);
+ do_check_eq(mozPermsV6Stmt.getInt64(2), it.permission);
+ do_check_eq(mozPermsV6Stmt.getInt64(3), it.expireType);
+ do_check_eq(mozPermsV6Stmt.getInt64(4), it.expireTime);
+ do_check_eq(mozPermsV6Stmt.getInt64(5), it.modificationTime);
+ });
+ } finally {
+ mozPermsV6Stmt.finalize();
+ }
// Check that there are the right number of values
let mozPermsV6Count = db.createStatement("SELECT count(*) FROM moz_perms_v6");
- mozPermsV6Count.executeStep();
- do_check_eq(mozPermsV6Count.getInt64(0), created5.length);
+ try {
+ mozPermsV6Count.executeStep();
+ do_check_eq(mozPermsV6Count.getInt64(0), created5.length);
+ } finally {
+ mozPermsV6Count.finalize();
+ }
db.close();
}
});
--- a/extensions/cookie/test/unit/test_permmanager_migrate_5-7b.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_5-7b.js
@@ -8,20 +8,16 @@ var PERMISSIONS_FILE_NAME = "permissions
function GetPermissionsFile(profile)
{
let file = profile.clone();
file.append(PERMISSIONS_FILE_NAME);
return file;
}
-function run_test() {
- run_next_test();
-}
-
add_task(function test() {
/* Create and set up the permissions database */
let profile = do_get_profile();
let db = Services.storage.openDatabase(GetPermissionsFile(profile));
db.schemaVersion = 5;
/*
@@ -53,17 +49,21 @@ add_task(function test() {
stmt5Insert.bindByName("id", thisId);
stmt5Insert.bindByName("origin", origin);
stmt5Insert.bindByName("type", type);
stmt5Insert.bindByName("permission", permission);
stmt5Insert.bindByName("expireType", expireType);
stmt5Insert.bindByName("expireTime", expireTime);
stmt5Insert.bindByName("modificationTime", modificationTime);
- stmt5Insert.execute();
+ try {
+ stmt5Insert.execute();
+ } finally {
+ stmt5Insert.reset();
+ }
return {
id: thisId,
host: origin,
type: type,
permission: permission,
expireType: expireType,
expireTime: expireTime,
@@ -137,32 +137,39 @@ add_task(function test() {
do_check_true(db.tableExists("moz_hosts"));
do_check_false(db.tableExists("moz_hosts_is_backup"));
do_check_false(db.tableExists("moz_perms_v6"));
let mozHostsStmt = db.createStatement("SELECT " +
"host, type, permission, expireType, expireTime, " +
"modificationTime, appId, isInBrowserElement " +
"FROM moz_hosts WHERE id = :id");
-
- // Check that the moz_hosts table still contains the correct values.
- created4.forEach((it) => {
- mozHostsStmt.reset();
- mozHostsStmt.bindByName("id", it.id);
- mozHostsStmt.executeStep();
- do_check_eq(mozHostsStmt.getUTF8String(0), it.host);
- do_check_eq(mozHostsStmt.getUTF8String(1), it.type);
- do_check_eq(mozHostsStmt.getInt64(2), it.permission);
- do_check_eq(mozHostsStmt.getInt64(3), it.expireType);
- do_check_eq(mozHostsStmt.getInt64(4), it.expireTime);
- do_check_eq(mozHostsStmt.getInt64(5), it.modificationTime);
- do_check_eq(mozHostsStmt.getInt64(6), it.appId);
- do_check_eq(mozHostsStmt.getInt64(7), it.isInBrowserElement);
- });
+ try {
+ // Check that the moz_hosts table still contains the correct values.
+ created4.forEach((it) => {
+ mozHostsStmt.reset();
+ mozHostsStmt.bindByName("id", it.id);
+ mozHostsStmt.executeStep();
+ do_check_eq(mozHostsStmt.getUTF8String(0), it.host);
+ do_check_eq(mozHostsStmt.getUTF8String(1), it.type);
+ do_check_eq(mozHostsStmt.getInt64(2), it.permission);
+ do_check_eq(mozHostsStmt.getInt64(3), it.expireType);
+ do_check_eq(mozHostsStmt.getInt64(4), it.expireTime);
+ do_check_eq(mozHostsStmt.getInt64(5), it.modificationTime);
+ do_check_eq(mozHostsStmt.getInt64(6), it.appId);
+ do_check_eq(mozHostsStmt.getInt64(7), it.isInBrowserElement);
+ });
+ } finally {
+ mozHostsStmt.finalize();
+ }
// Check that there are the right number of values
let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts");
- mozHostsCount.executeStep();
- do_check_eq(mozHostsCount.getInt64(0), created4.length);
+ try {
+ mozHostsCount.executeStep();
+ do_check_eq(mozHostsCount.getInt64(0), created4.length);
+ } finally {
+ mozHostsCount.finalize();
+ }
db.close();
}
});
--- a/extensions/cookie/test/unit/test_permmanager_migrate_6-7a.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_6-7a.js
@@ -8,20 +8,16 @@ var PERMISSIONS_FILE_NAME = "permissions
function GetPermissionsFile(profile)
{
let file = profile.clone();
file.append(PERMISSIONS_FILE_NAME);
return file;
}
-function run_test() {
- run_next_test();
-}
-
add_task(function* test() {
/* Create and set up the permissions database */
let profile = do_get_profile();
let db = Services.storage.openDatabase(GetPermissionsFile(profile));
db.schemaVersion = 6;
/*
@@ -76,17 +72,21 @@ add_task(function* test() {
stmt6Insert.bindByName("id", thisId);
stmt6Insert.bindByName("origin", origin);
stmt6Insert.bindByName("type", type);
stmt6Insert.bindByName("permission", permission);
stmt6Insert.bindByName("expireType", expireType);
stmt6Insert.bindByName("expireTime", expireTime);
stmt6Insert.bindByName("modificationTime", modificationTime);
- stmt6Insert.execute();
+ try {
+ stmt6Insert.execute();
+ } finally {
+ stmt6Insert.reset();
+ }
return {
id: thisId,
origin: origin,
type: type,
permission: permission,
expireType: expireType,
expireTime: expireTime,
@@ -102,17 +102,21 @@ add_task(function* test() {
stmtInsert.bindByName("type", type);
stmtInsert.bindByName("permission", permission);
stmtInsert.bindByName("expireType", expireType);
stmtInsert.bindByName("expireTime", expireTime);
stmtInsert.bindByName("modificationTime", modificationTime);
stmtInsert.bindByName("appId", appId);
stmtInsert.bindByName("isInBrowserElement", isInBrowserElement);
- stmtInsert.execute();
+ try {
+ stmtInsert.execute();
+ } finally {
+ stmtInsert.reset();
+ }
return {
id: thisId,
host: host,
type: type,
permission: permission,
expireType: expireType,
expireTime: expireTime,
@@ -146,16 +150,17 @@ add_task(function* test() {
insertHost("file:///another/file.html", "A", 1, 0, 0, 0, 0, false),
insertHost("moz-nullprincipal:{8695105a-adbe-4e4e-8083-851faa5ca2d7}", "A", 1, 0, 0, 0, 0, false),
insertHost("moz-nullprincipal:{12ahjksd-akjs-asd3-8393-asdu2189asdu}", "B", 1, 0, 0, 0, 0, false),
insertHost("<file>", "A", 1, 0, 0, 0, 0, false),
insertHost("<file>", "B", 1, 0, 0, 0, 0, false),
];
// CLose the db connection
+ stmt6Insert.finalize();
stmtInsert.finalize();
db.close();
stmtInsert = null;
db = null;
let expected = [
// The http:// entries under foo.com won't be inserted, as there are history entries for foo.com,
// and http://foo.com or a subdomain are never visited.
@@ -248,37 +253,48 @@ add_task(function* test() {
let db = Services.storage.openDatabase(GetPermissionsFile(profile));
do_check_true(db.tableExists("moz_perms"));
do_check_true(db.tableExists("moz_hosts"));
do_check_false(db.tableExists("moz_hosts_is_backup"));
do_check_true(db.tableExists("moz_perms_v6"));
// The moz_hosts table should still exist but be empty
let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts");
- mozHostsCount.executeStep();
- do_check_eq(mozHostsCount.getInt64(0), 0);
+ try {
+ mozHostsCount.executeStep();
+ do_check_eq(mozHostsCount.getInt64(0), 0);
+ } finally {
+ mozHostsCount.finalize();
+ }
// Check that the moz_perms_v6 table contains the backup of the entry we created
let mozPermsV6Stmt = db.createStatement("SELECT " +
"origin, type, permission, expireType, expireTime, modificationTime " +
"FROM moz_perms_v6 WHERE id = :id");
-
- // Check that the moz_hosts table still contains the correct values.
- created6.forEach((it) => {
- mozPermsV6Stmt.reset();
- mozPermsV6Stmt.bindByName("id", it.id);
- mozPermsV6Stmt.executeStep();
- do_check_eq(mozPermsV6Stmt.getUTF8String(0), it.origin);
- do_check_eq(mozPermsV6Stmt.getUTF8String(1), it.type);
- do_check_eq(mozPermsV6Stmt.getInt64(2), it.permission);
- do_check_eq(mozPermsV6Stmt.getInt64(3), it.expireType);
- do_check_eq(mozPermsV6Stmt.getInt64(4), it.expireTime);
- do_check_eq(mozPermsV6Stmt.getInt64(5), it.modificationTime);
- });
+ try {
+ // Check that the moz_hosts table still contains the correct values.
+ created6.forEach((it) => {
+ mozPermsV6Stmt.reset();
+ mozPermsV6Stmt.bindByName("id", it.id);
+ mozPermsV6Stmt.executeStep();
+ do_check_eq(mozPermsV6Stmt.getUTF8String(0), it.origin);
+ do_check_eq(mozPermsV6Stmt.getUTF8String(1), it.type);
+ do_check_eq(mozPermsV6Stmt.getInt64(2), it.permission);
+ do_check_eq(mozPermsV6Stmt.getInt64(3), it.expireType);
+ do_check_eq(mozPermsV6Stmt.getInt64(4), it.expireTime);
+ do_check_eq(mozPermsV6Stmt.getInt64(5), it.modificationTime);
+ });
+ } finally {
+ mozPermsV6Stmt.finalize();
+ }
// Check that there are the right number of values
let mozPermsV6Count = db.createStatement("SELECT count(*) FROM moz_perms_v6");
- mozPermsV6Count.executeStep();
- do_check_eq(mozPermsV6Count.getInt64(0), created6.length);
+ try {
+ mozPermsV6Count.executeStep();
+ do_check_eq(mozPermsV6Count.getInt64(0), created6.length);
+ } finally {
+ mozPermsV6Count.finalize();
+ }
db.close();
}
});
--- a/extensions/cookie/test/unit/test_permmanager_migrate_6-7b.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_6-7b.js
@@ -8,20 +8,16 @@ var PERMISSIONS_FILE_NAME = "permissions
function GetPermissionsFile(profile)
{
let file = profile.clone();
file.append(PERMISSIONS_FILE_NAME);
return file;
}
-function run_test() {
- run_next_test();
-}
-
add_task(function test() {
/* Create and set up the permissions database */
let profile = do_get_profile();
let db = Services.storage.openDatabase(GetPermissionsFile(profile));
db.schemaVersion = 6;
/*
@@ -53,17 +49,21 @@ add_task(function test() {
stmt6Insert.bindByName("id", thisId);
stmt6Insert.bindByName("origin", origin);
stmt6Insert.bindByName("type", type);
stmt6Insert.bindByName("permission", permission);
stmt6Insert.bindByName("expireType", expireType);
stmt6Insert.bindByName("expireTime", expireTime);
stmt6Insert.bindByName("modificationTime", modificationTime);
- stmt6Insert.execute();
+ try {
+ stmt6Insert.execute();
+ } finally {
+ stmt6Insert.reset();
+ }
return {
id: thisId,
host: origin,
type: type,
permission: permission,
expireType: expireType,
expireTime: expireTime,
@@ -131,32 +131,39 @@ add_task(function test() {
do_check_true(db.tableExists("moz_hosts"));
do_check_false(db.tableExists("moz_hosts_is_backup"));
do_check_false(db.tableExists("moz_perms_v6"));
let mozHostsStmt = db.createStatement("SELECT " +
"host, type, permission, expireType, expireTime, " +
"modificationTime, appId, isInBrowserElement " +
"FROM moz_hosts WHERE id = :id");
-
- // Check that the moz_hosts table still contains the correct values.
- created4.forEach((it) => {
- mozHostsStmt.reset();
- mozHostsStmt.bindByName("id", it.id);
- mozHostsStmt.executeStep();
- do_check_eq(mozHostsStmt.getUTF8String(0), it.host);
- do_check_eq(mozHostsStmt.getUTF8String(1), it.type);
- do_check_eq(mozHostsStmt.getInt64(2), it.permission);
- do_check_eq(mozHostsStmt.getInt64(3), it.expireType);
- do_check_eq(mozHostsStmt.getInt64(4), it.expireTime);
- do_check_eq(mozHostsStmt.getInt64(5), it.modificationTime);
- do_check_eq(mozHostsStmt.getInt64(6), it.appId);
- do_check_eq(mozHostsStmt.getInt64(7), it.isInBrowserElement);
- });
+ try {
+ // Check that the moz_hosts table still contains the correct values.
+ created4.forEach((it) => {
+ mozHostsStmt.reset();
+ mozHostsStmt.bindByName("id", it.id);
+ mozHostsStmt.executeStep();
+ do_check_eq(mozHostsStmt.getUTF8String(0), it.host);
+ do_check_eq(mozHostsStmt.getUTF8String(1), it.type);
+ do_check_eq(mozHostsStmt.getInt64(2), it.permission);
+ do_check_eq(mozHostsStmt.getInt64(3), it.expireType);
+ do_check_eq(mozHostsStmt.getInt64(4), it.expireTime);
+ do_check_eq(mozHostsStmt.getInt64(5), it.modificationTime);
+ do_check_eq(mozHostsStmt.getInt64(6), it.appId);
+ do_check_eq(mozHostsStmt.getInt64(7), it.isInBrowserElement);
+ });
+ } finally {
+ mozHostsStmt.finalize();
+ }
// Check that there are the right number of values
let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts");
- mozHostsCount.executeStep();
- do_check_eq(mozHostsCount.getInt64(0), created4.length);
+ try {
+ mozHostsCount.executeStep();
+ do_check_eq(mozHostsCount.getInt64(0), created4.length);
+ } finally {
+ mozHostsCount.finalize();
+ }
db.close();
}
});
--- a/extensions/cookie/test/unit/test_permmanager_migrate_7-8.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_7-8.js
@@ -8,20 +8,16 @@ var PERMISSIONS_FILE_NAME = "permissions
function GetPermissionsFile(profile)
{
let file = profile.clone();
file.append(PERMISSIONS_FILE_NAME);
return file;
}
-function run_test() {
- run_next_test();
-}
-
add_task(function* test() {
/* Create and set up the permissions database */
let profile = do_get_profile();
let db = Services.storage.openDatabase(GetPermissionsFile(profile));
db.schemaVersion = 7;
/*
@@ -81,17 +77,21 @@ add_task(function* test() {
stmt6Insert.bindByName("id", thisId);
stmt6Insert.bindByName("origin", origin);
stmt6Insert.bindByName("type", type);
stmt6Insert.bindByName("permission", permission);
stmt6Insert.bindByName("expireType", expireType);
stmt6Insert.bindByName("expireTime", expireTime);
stmt6Insert.bindByName("modificationTime", modificationTime);
- stmt6Insert.execute();
+ try {
+ stmt6Insert.execute();
+ } finally {
+ stmt6Insert.reset();
+ }
return {
id: thisId,
origin: origin,
type: type,
permission: permission,
expireType: expireType,
expireTime: expireTime,
@@ -107,17 +107,21 @@ add_task(function* test() {
stmtInsert.bindByName("type", type);
stmtInsert.bindByName("permission", permission);
stmtInsert.bindByName("expireType", expireType);
stmtInsert.bindByName("expireTime", expireTime);
stmtInsert.bindByName("modificationTime", modificationTime);
stmtInsert.bindByName("appId", appId);
stmtInsert.bindByName("isInBrowserElement", isInBrowserElement);
- stmtInsert.execute();
+ try {
+ stmtInsert.execute();
+ } finally {
+ stmtInsert.reset();
+ }
return {
id: thisId,
host: host,
type: type,
permission: permission,
expireType: expireType,
expireTime: expireTime,
@@ -156,16 +160,17 @@ add_task(function* test() {
insertHost("file:///another/file.html", "A", 1, 0, 0, 0, 0, false),
insertHost("moz-nullprincipal:{8695105a-adbe-4e4e-8083-851faa5ca2d7}", "A", 1, 0, 0, 0, 0, false),
insertHost("moz-nullprincipal:{12ahjksd-akjs-asd3-8393-asdu2189asdu}", "B", 1, 0, 0, 0, 0, false),
insertHost("<file>", "A", 1, 0, 0, 0, 0, false),
insertHost("<file>", "B", 1, 0, 0, 0, 0, false),
];
// CLose the db connection
+ stmt6Insert.finalize();
stmtInsert.finalize();
db.close();
stmtInsert = null;
db = null;
let expected = [
// We should have kept the previously migrated entries
["https://foo.com", "A", 2, 0, 0, 0],
@@ -228,19 +233,27 @@ add_task(function* test() {
let db = Services.storage.openDatabase(GetPermissionsFile(profile));
do_check_true(db.tableExists("moz_perms"));
do_check_true(db.tableExists("moz_hosts"));
do_check_false(db.tableExists("moz_hosts_is_backup"));
do_check_false(db.tableExists("moz_perms_v6"));
// The moz_hosts table should still exist but be empty
let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts");
- mozHostsCount.executeStep();
- do_check_eq(mozHostsCount.getInt64(0), 0);
+ try {
+ mozHostsCount.executeStep();
+ do_check_eq(mozHostsCount.getInt64(0), 0);
+ } finally {
+ mozHostsCount.finalize();
+ }
// Check that there are the right number of values in the permissions database
let mozPermsCount = db.createStatement("SELECT count(*) FROM moz_perms");
- mozPermsCount.executeStep();
- do_check_eq(mozPermsCount.getInt64(0), expected.length);
+ try {
+ mozPermsCount.executeStep();
+ do_check_eq(mozPermsCount.getInt64(0), expected.length);
+ } finally {
+ mozPermsCount.finalize();
+ }
db.close();
}
});
--- a/netwerk/cookie/test/unit/test_bug1321912.js
+++ b/netwerk/cookie/test/unit/test_bug1321912.js
@@ -51,28 +51,35 @@ conn.executeSimpleSQL("INSERT INTO moz_c
// Now start the cookie service, and then check the fields in the table.
const cs = Cc["@mozilla.org/cookieService;1"].
getService(Ci.nsICookieService);
do_check_true(conn.schemaVersion, 8);
let stmt = conn.createStatement("SELECT sql FROM sqlite_master " +
- "WHERE type = 'table' AND " +
- " name = 'moz_cookies'");
-do_check_true(stmt.executeStep());
-let sql = stmt.getString(0);
-do_check_eq(sql.indexOf("appId"), -1);
+ "WHERE type = 'table' AND " +
+ " name = 'moz_cookies'");
+try {
+ do_check_true(stmt.executeStep());
+ let sql = stmt.getString(0);
+ do_check_eq(sql.indexOf("appId"), -1);
+} finally {
+ stmt.finalize();
+}
stmt = conn.createStatement("SELECT * FROM moz_cookies " +
"WHERE baseDomain = 'foo.com' AND " +
" host = '.foo.com' AND " +
" name = 'foo' AND " +
" value = 'bar=baz' AND " +
" path = '/' AND " +
" expiry = " + now + " AND " +
" lastAccessed = " + now + " AND " +
" creationTime = " + now + " AND " +
" isSecure = 1 AND " +
" isHttpOnly = 1");
-do_check_true(stmt.executeStep());
-
+try {
+ do_check_true(stmt.executeStep());
+} finally {
+ stmt.finalize();
+}
conn.close();
--- a/services/common/tests/unit/test_async_querySpinningly.js
+++ b/services/common/tests/unit/test_async_querySpinningly.js
@@ -14,26 +14,27 @@ const SQLITE_CONSTRAINT_VIOLATION = 19;
// This test is a bit hacky - it was originally written to use the
// formhistory.sqlite database using the nsIFormHistory2 sync APIs. However,
// that's now been deprecated in favour of the async FormHistory.jsm.
// Rather than re-write the test completely, we cheat - we use FormHistory.jsm
// to initialize the database, then we just re-open it for these tests.
// Init the forms database.
FormHistory.schemaVersion;
+FormHistory.shutdown();
// and open the database it just created.
let dbFile = Services.dirsvc.get("ProfD", Ci.nsIFile).clone();
dbFile.append("formhistory.sqlite");
let dbConnection = Services.storage.openUnsharedDatabase(dbFile);
do_register_cleanup(() => {
let cb = Async.makeSpinningCallback();
dbConnection.asyncClose(cb);
- cb.wait();
+ return cb.wait();
});
function querySpinningly(query, names) {
let q = dbConnection.createStatement(query);
let r = Async.querySpinningly(q, names);
q.finalize();
return r;
}
--- a/storage/SQLiteMutex.h
+++ b/storage/SQLiteMutex.h
@@ -45,31 +45,39 @@ public:
*/
void initWithMutex(sqlite3_mutex *aMutex)
{
NS_ASSERTION(aMutex, "You must pass in a valid mutex!");
NS_ASSERTION(!mMutex, "A mutex has already been set for this!");
mMutex = aMutex;
}
+ /**
+ * After a connection has been successfully closed, its mutex is a dangling
+ * pointer, and as such it should be destroyed.
+ */
+ void destroy() {
+ mMutex = NULL;
+ }
+
#if !defined(DEBUG) || defined(MOZ_SYSTEM_SQLITE)
/**
* Acquires the mutex.
*/
void lock()
{
- sqlite3_mutex_enter(mMutex);
+ ::sqlite3_mutex_enter(mMutex);
}
/**
* Releases the mutex.
*/
void unlock()
{
- sqlite3_mutex_leave(mMutex);
+ ::sqlite3_mutex_leave(mMutex);
}
/**
* Asserts that the current thread owns the mutex.
*/
void assertCurrentThreadOwns()
{
}
@@ -79,48 +87,48 @@ public:
*/
void assertNotCurrentThreadOwns()
{
}
#else
void lock()
{
- NS_ASSERTION(mMutex, "No mutex associated with this wrapper!");
+ MOZ_ASSERT(mMutex, "No mutex associated with this wrapper!");
// While SQLite Mutexes may be recursive, in our own code we do not want to
// treat them as such.
CheckAcquire();
- sqlite3_mutex_enter(mMutex);
+ ::sqlite3_mutex_enter(mMutex);
Acquire(); // Call is protected by us holding the mutex.
}
void unlock()
{
- NS_ASSERTION(mMutex, "No mutex associated with this wrapper!");
+ MOZ_ASSERT(mMutex, "No mutex associated with this wrapper!");
// While SQLite Mutexes may be recursive, in our own code we do not want to
// treat them as such.
Release(); // Call is protected by us holding the mutex.
- sqlite3_mutex_leave(mMutex);
+ ::sqlite3_mutex_leave(mMutex);
}
void assertCurrentThreadOwns()
{
- NS_ASSERTION(mMutex, "No mutex associated with this wrapper!");
- NS_ASSERTION(sqlite3_mutex_held(mMutex),
- "Mutex is not held, but we expect it to be!");
+ MOZ_ASSERT(mMutex, "No mutex associated with this wrapper!");
+ MOZ_ASSERT(sqlite3_mutex_held(mMutex),
+ "Mutex is not held, but we expect it to be!");
}
void assertNotCurrentThreadOwns()
{
- NS_ASSERTION(mMutex, "No mutex associated with this wrapper!");
- NS_ASSERTION(sqlite3_mutex_notheld(mMutex),
- "Mutex is held, but we expect it to not be!");
+ MOZ_ASSERT(mMutex, "No mutex associated with this wrapper!");
+ MOZ_ASSERT(sqlite3_mutex_notheld(mMutex),
+ "Mutex is held, but we expect it to not be!");
}
#endif // ifndef DEBUG
private:
sqlite3_mutex *mMutex;
};
/**
--- a/storage/mozStorageAsyncStatement.cpp
+++ b/storage/mozStorageAsyncStatement.cpp
@@ -113,17 +113,17 @@ AsyncStatement::AsyncStatement()
}
nsresult
AsyncStatement::initialize(Connection *aDBConnection,
sqlite3 *aNativeConnection,
const nsACString &aSQLStatement)
{
MOZ_ASSERT(aDBConnection, "No database connection given!");
- MOZ_ASSERT(!aDBConnection->isClosed(), "Database connection should be valid");
+ MOZ_ASSERT(aDBConnection->isConnectionReadyOnThisThread(), "Database connection should be valid");
MOZ_ASSERT(aNativeConnection, "No native connection given!");
mDBConnection = aDBConnection;
mNativeConnection = aNativeConnection;
mSQLString = aSQLStatement;
MOZ_LOG(gStorageLog, LogLevel::Debug, ("Inited async statement '%s' (0x%p)",
mSQLString.get(), this));
--- a/storage/mozStorageAsyncStatementExecution.cpp
+++ b/storage/mozStorageAsyncStatementExecution.cpp
@@ -578,17 +578,17 @@ AsyncExecuteStatements::Cancel()
}
////////////////////////////////////////////////////////////////////////////////
//// nsIRunnable
NS_IMETHODIMP
AsyncExecuteStatements::Run()
{
- MOZ_ASSERT(!mConnection->isClosed());
+ MOZ_ASSERT(mConnection->isConnectionReadyOnThisThread());
// Do not run if we have been canceled.
{
MutexAutoLock lockedScope(mMutex);
if (mCancelRequested)
mState = CANCELED;
}
if (mState == CANCELED)
--- a/storage/mozStorageConnection.cpp
+++ b/storage/mozStorageConnection.cpp
@@ -15,16 +15,17 @@
#include "nsIFileURL.h"
#include "mozilla/Telemetry.h"
#include "mozilla/Mutex.h"
#include "mozilla/CondVar.h"
#include "mozilla/Attributes.h"
#include "mozilla/ErrorNames.h"
#include "mozilla/Unused.h"
#include "mozilla/dom/quota/QuotaObject.h"
+#include "mozilla/ScopeExit.h"
#include "mozIStorageAggregateFunction.h"
#include "mozIStorageCompletionCallback.h"
#include "mozIStorageFunction.h"
#include "mozStorageAsyncStatementExecution.h"
#include "mozStorageSQLFunctions.h"
#include "mozStorageConnection.h"
@@ -375,37 +376,31 @@ WaitForUnlockNotify(sqlite3* aDatabase)
namespace {
class AsyncCloseConnection final: public Runnable
{
public:
AsyncCloseConnection(Connection *aConnection,
sqlite3 *aNativeConnection,
- nsIRunnable *aCallbackEvent,
- already_AddRefed<nsIThread> aAsyncExecutionThread)
+ nsIRunnable *aCallbackEvent)
: mConnection(aConnection)
, mNativeConnection(aNativeConnection)
, mCallbackEvent(aCallbackEvent)
- , mAsyncExecutionThread(aAsyncExecutionThread)
{
}
NS_IMETHOD Run() override
{
-#ifdef DEBUG
// This code is executed on the background thread
- bool onAsyncThread = false;
- (void)mAsyncExecutionThread->IsOnCurrentThread(&onAsyncThread);
- MOZ_ASSERT(onAsyncThread);
-#endif // DEBUG
+ MOZ_ASSERT(NS_GetCurrentThread() != mConnection->threadOpenedOn);
- nsCOMPtr<nsIRunnable> event = NewRunnableMethod<nsCOMPtr<nsIThread>>
- (mConnection, &Connection::shutdownAsyncThread, mAsyncExecutionThread);
- (void)NS_DispatchToMainThread(event);
+ nsCOMPtr<nsIRunnable> event =
+ NewRunnableMethod(mConnection, &Connection::shutdownAsyncThread);
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(event));
// Internal close.
(void)mConnection->internalClose(mNativeConnection);
// Callback
if (mCallbackEvent) {
nsCOMPtr<nsIThread> thread;
(void)NS_GetMainThread(getter_AddRefs(thread));
@@ -418,17 +413,16 @@ public:
~AsyncCloseConnection() override {
NS_ReleaseOnMainThread(mConnection.forget());
NS_ReleaseOnMainThread(mCallbackEvent.forget());
}
private:
RefPtr<Connection> mConnection;
sqlite3 *mNativeConnection;
nsCOMPtr<nsIRunnable> mCallbackEvent;
- nsCOMPtr<nsIThread> mAsyncExecutionThread;
};
/**
* An event used to initialize the clone of a connection.
*
* Must be executed on the clone's async execution thread.
*/
class AsyncInitializeClone final: public Runnable
@@ -503,19 +497,16 @@ Connection::Connection(Service *aService
int aFlags,
bool aAsyncOnly,
bool aIgnoreLockingMode)
: sharedAsyncExecutionMutex("Connection::sharedAsyncExecutionMutex")
, sharedDBMutex("Connection::sharedDBMutex")
, threadOpenedOn(do_GetCurrentThread())
, mDBConn(nullptr)
, mAsyncExecutionThreadShuttingDown(false)
-#ifdef DEBUG
-, mAsyncExecutionThreadIsAlive(false)
-#endif
, mConnectionClosed(false)
, mTransactionInProgress(false)
, mProgressHandler(nullptr)
, mFlags(aFlags)
, mIgnoreLockingMode(aIgnoreLockingMode)
, mStorageService(aService)
, mAsyncOnly(aAsyncOnly)
{
@@ -524,19 +515,17 @@ Connection::Connection(Service *aService
mStorageService->registerConnection(this);
}
Connection::~Connection()
{
(void)Close();
MOZ_ASSERT(!mAsyncExecutionThread,
- "AsyncClose has not been invoked on this connection!");
- MOZ_ASSERT(!mAsyncExecutionThreadIsAlive,
- "The async execution thread should have been shutdown!");
+ "The async thread has not been shutdown properly!");
}
NS_IMPL_ADDREF(Connection)
NS_INTERFACE_MAP_BEGIN(Connection)
NS_INTERFACE_MAP_ENTRY(mozIStorageAsyncConnection)
NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(mozIStorageConnection, !mAsyncOnly)
@@ -576,37 +565,34 @@ Connection::getSqliteRuntimeStatus(int32
if (aMaxValue)
*aMaxValue = max;
return curr;
}
nsIEventTarget *
Connection::getAsyncExecutionTarget()
{
- NS_ENSURE_TRUE(NS_IsMainThread(), nullptr);
+ NS_ENSURE_TRUE(threadOpenedOn == NS_GetCurrentThread(), nullptr);
- // If we are shutting down the asynchronous thread, don't hand out any more
- // references to the thread.
- if (mAsyncExecutionThreadShuttingDown)
+ // Don't return the asynchronous thread if we are shutting down.
+ if (mAsyncExecutionThreadShuttingDown) {
return nullptr;
+ }
+ // Create the async thread if there's none yet.
if (!mAsyncExecutionThread) {
static nsThreadPoolNaming naming;
nsresult rv = NS_NewNamedThread(naming.GetNextThreadName("mozStorage"),
getter_AddRefs(mAsyncExecutionThread));
if (NS_FAILED(rv)) {
NS_WARNING("Failed to create async thread.");
return nullptr;
}
}
-#ifdef DEBUG
- mAsyncExecutionThreadIsAlive = true;
-#endif
-
return mAsyncExecutionThread;
}
nsresult
Connection::initialize()
{
NS_ASSERTION (!mDBConn, "Initialize called on already opened database!");
MOZ_ASSERT(!mIgnoreLockingMode, "Can't ignore locking on an in-memory db.");
@@ -699,16 +685,26 @@ Connection::initialize(nsIFileURL *aFile
return NS_OK;
}
nsresult
Connection::initializeInternal()
{
MOZ_ASSERT(mDBConn);
+ auto guard = MakeScopeExit([&]() {
+ {
+ MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
+ mConnectionClosed = true;
+ }
+ MOZ_ALWAYS_TRUE(::sqlite3_close(mDBConn) == SQLITE_OK);
+ mDBConn = nullptr;
+ sharedDBMutex.destroy();
+ });
+
if (mFileURL) {
const char* dbPath = ::sqlite3_db_filename(mDBConn, "main");
MOZ_ASSERT(dbPath);
const char* telemetryFilename =
::sqlite3_uri_parameter(dbPath, "telemetryFilename");
if (telemetryFilename) {
if (NS_WARN_IF(*telemetryFilename == '\0')) {
@@ -740,49 +736,45 @@ Connection::initializeInternal()
int64_t pageSize = Service::getDefaultPageSize();
// Set page_size to the preferred default value. This is effective only if
// the database has just been created, otherwise, if the database does not
// use WAL journal mode, a VACUUM operation will updated its page_size.
nsAutoCString pageSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
"PRAGMA page_size = ");
pageSizeQuery.AppendInt(pageSize);
- nsresult rv = ExecuteSimpleSQL(pageSizeQuery);
- NS_ENSURE_SUCCESS(rv, rv);
+ int srv = executeSql(mDBConn, pageSizeQuery.get());
+ if (srv != SQLITE_OK) {
+ return convertResultCode(srv);
+ }
// Setting the cache_size forces the database open, verifying if it is valid
// or corrupt. So this is executed regardless it being actually needed.
// The cache_size is calculated from the actual page_size, to save memory.
nsAutoCString cacheSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
"PRAGMA cache_size = ");
cacheSizeQuery.AppendInt(-MAX_CACHE_SIZE_KIBIBYTES);
- int srv = executeSql(mDBConn, cacheSizeQuery.get());
+ srv = executeSql(mDBConn, cacheSizeQuery.get());
if (srv != SQLITE_OK) {
- ::sqlite3_close(mDBConn);
- mDBConn = nullptr;
return convertResultCode(srv);
}
#if defined(MOZ_MEMORY_TEMP_STORE_PRAGMA)
(void)ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA temp_store = 2;"));
#endif
// Register our built-in SQL functions.
srv = registerFunctions(mDBConn);
if (srv != SQLITE_OK) {
- ::sqlite3_close(mDBConn);
- mDBConn = nullptr;
return convertResultCode(srv);
}
// Register our built-in SQL collating sequences.
srv = registerCollations(mDBConn, mStorageService);
if (srv != SQLITE_OK) {
- ::sqlite3_close(mDBConn);
- mDBConn = nullptr;
return convertResultCode(srv);
}
// Set the synchronous PRAGMA, according to the preference.
switch (Service::getSynchronousPref()) {
case 2:
(void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"PRAGMA synchronous = FULL;"));
@@ -793,20 +785,38 @@ Connection::initializeInternal()
break;
case 1:
default:
(void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"PRAGMA synchronous = NORMAL;"));
break;
}
+ // Initialization succeeded, we can stop guarding for failures.
+ guard.release();
return NS_OK;
}
nsresult
+Connection::initializeOnAsyncThread(nsIFile* aStorageFile) {
+ MOZ_ASSERT(threadOpenedOn != NS_GetCurrentThread());
+ nsresult rv = aStorageFile ? initialize(aStorageFile)
+ : initialize();
+ if (NS_FAILED(rv)) {
+ // Shutdown the async thread, since initialization failed.
+ MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
+ mAsyncExecutionThreadShuttingDown = true;
+ nsCOMPtr<nsIRunnable> event =
+ NewRunnableMethod(this, &Connection::shutdownAsyncThread);
+ Unused << NS_DispatchToMainThread(event);
+ }
+ return rv;
+}
+
+nsresult
Connection::databaseElementExists(enum DatabaseElementType aElementType,
const nsACString &aElementName,
bool *_exists)
{
if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
// When constructing the query, make sure to SELECT the correct db's sqlite_master
// if the user is prefixing the element with a specific db. ex: sample.test
@@ -902,82 +912,87 @@ Connection::setClosedState()
}
// Flag that we are shutting down the async thread, so that
// getAsyncExecutionTarget knows not to expose/create the async thread.
{
MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
NS_ENSURE_FALSE(mAsyncExecutionThreadShuttingDown, NS_ERROR_UNEXPECTED);
mAsyncExecutionThreadShuttingDown = true;
+
+ // Set the property to null before closing the connection, otherwise the other
+ // functions in the module may try to use the connection after it is closed.
+ mDBConn = nullptr;
}
-
- // Set the property to null before closing the connection, otherwise the other
- // functions in the module may try to use the connection after it is closed.
- mDBConn = nullptr;
-
return NS_OK;
}
bool
Connection::connectionReady()
{
return mDBConn != nullptr;
}
bool
+Connection::isConnectionReadyOnThisThread()
+{
+ MOZ_ASSERT_IF(mDBConn, !mConnectionClosed);
+ if (mAsyncExecutionThread &&
+ mAsyncExecutionThread->IsOnCurrentThread()) {
+ return true;
+ }
+ return connectionReady();
+}
+
+bool
Connection::isClosing()
{
- bool shuttingDown = false;
- {
- MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
- shuttingDown = mAsyncExecutionThreadShuttingDown;
- }
- return shuttingDown && !isClosed();
+ MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
+ return mAsyncExecutionThreadShuttingDown && !mConnectionClosed;
}
bool
Connection::isClosed()
{
MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
return mConnectionClosed;
}
bool
+Connection::isClosed(MutexAutoLock& lock)
+{
+ return mConnectionClosed;
+}
+
+bool
Connection::isAsyncExecutionThreadAvailable()
{
- MOZ_ASSERT(NS_IsMainThread());
- return !!mAsyncExecutionThread;
+ MOZ_ASSERT(threadOpenedOn == NS_GetCurrentThread());
+ return mAsyncExecutionThread && !mAsyncExecutionThreadShuttingDown;
}
void
-Connection::shutdownAsyncThread(nsIThread *aThread) {
- MOZ_ASSERT(!mAsyncExecutionThread);
- MOZ_ASSERT(mAsyncExecutionThreadIsAlive);
+Connection::shutdownAsyncThread() {
+ MOZ_ASSERT(threadOpenedOn == NS_GetCurrentThread());
+ MOZ_ASSERT(mAsyncExecutionThread);
MOZ_ASSERT(mAsyncExecutionThreadShuttingDown);
- DebugOnly<nsresult> rv = aThread->Shutdown();
- MOZ_ASSERT(NS_SUCCEEDED(rv));
-#ifdef DEBUG
- mAsyncExecutionThreadIsAlive = false;
-#endif
+ MOZ_ALWAYS_SUCCEEDS(mAsyncExecutionThread->Shutdown());
+ mAsyncExecutionThread = nullptr;
}
nsresult
Connection::internalClose(sqlite3 *aNativeConnection)
{
- // Sanity checks to make sure we are in the proper state before calling this.
- // aNativeConnection can be null if OpenAsyncDatabase failed and is now just
- // cleaning up the async thread.
- MOZ_ASSERT(!isClosed());
-
#ifdef DEBUG
{ // Make sure we have marked our async thread as shutting down.
MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
- NS_ASSERTION(mAsyncExecutionThreadShuttingDown,
- "Did not call setClosedState!");
+ MOZ_ASSERT(mAsyncExecutionThreadShuttingDown,
+ "Did not call setClosedState!");
+ MOZ_ASSERT(!isClosed(lockedScope), "Unexpected closed state");
}
#endif // DEBUG
if (MOZ_LOG_TEST(gStorageLog, LogLevel::Debug)) {
nsAutoCString leafName(":memory");
if (mDatabaseFile)
(void)mDatabaseFile->GetNativeLeafName(leafName);
MOZ_LOG(gStorageLog, LogLevel::Debug, ("Closing connection to '%s'",
@@ -994,21 +1009,23 @@ Connection::internalClose(sqlite3 *aNati
MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
mConnectionClosed = true;
}
// Nothing else needs to be done if we don't have a connection here.
if (!aNativeConnection)
return NS_OK;
- int srv = sqlite3_close(aNativeConnection);
+ int srv = ::sqlite3_close(aNativeConnection);
if (srv == SQLITE_BUSY) {
+ // Nothing else should change the connection or statements status until we
+ // are done here.
+ SQLiteMutexAutoLock lockedScope(sharedDBMutex);
// We still have non-finalized statements. Finalize them.
-
sqlite3_stmt *stmt = nullptr;
while ((stmt = ::sqlite3_next_stmt(aNativeConnection, stmt))) {
MOZ_LOG(gStorageLog, LogLevel::Debug,
("Auto-finalizing SQL statement '%s' (%p)",
::sqlite3_sql(stmt),
stmt));
#ifdef DEBUG
@@ -1036,21 +1053,22 @@ Connection::internalClose(sqlite3 *aNati
if (srv == SQLITE_OK) {
stmt = nullptr;
}
}
// Now that all statements have been finalized, we
// should be able to close.
srv = ::sqlite3_close(aNativeConnection);
-
}
- if (srv != SQLITE_OK) {
- MOZ_ASSERT(srv == SQLITE_OK,
+ if (srv == SQLITE_OK) {
+ sharedDBMutex.destroy();
+ } else {
+ MOZ_ASSERT(false,
"sqlite3_close failed. There are probably outstanding statements that are listed above!");
}
return convertResultCode(srv);
}
nsCString
Connection::getFilename()
@@ -1069,17 +1087,17 @@ Connection::stepStatement(sqlite3 *aNati
bool checkedMainThread = false;
TimeStamp startTime = TimeStamp::Now();
// The connection may have been closed if the executing statement has been
// created and cached after a call to asyncClose() but before the actual
// sqlite3_close(). This usually happens when other tasks using cached
// statements are asynchronously scheduled for execution and any of them ends
// up after asyncClose. See bug 728653 for details.
- if (isClosed())
+ if (!isConnectionReadyOnThisThread())
return SQLITE_MISUSE;
(void)::sqlite3_extended_result_codes(aNativeConnection, 1);
int srv;
while ((srv = ::sqlite3_step(aStatement)) == SQLITE_LOCKED_SHAREDCACHE) {
if (!checkedMainThread) {
checkedMainThread = true;
@@ -1114,17 +1132,17 @@ Connection::stepStatement(sqlite3 *aNati
}
int
Connection::prepareStatement(sqlite3 *aNativeConnection, const nsCString &aSQL,
sqlite3_stmt **_stmt)
{
// We should not even try to prepare statements after the connection has
// been closed.
- if (isClosed())
+ if (!isConnectionReadyOnThisThread())
return SQLITE_MISUSE;
bool checkedMainThread = false;
(void)::sqlite3_extended_result_codes(aNativeConnection, 1);
int srv;
while((srv = ::sqlite3_prepare_v2(aNativeConnection,
@@ -1171,17 +1189,17 @@ Connection::prepareStatement(sqlite3 *aN
return rc;
}
int
Connection::executeSql(sqlite3 *aNativeConnection, const char *aSqlString)
{
- if (isClosed())
+ if (!isConnectionReadyOnThisThread())
return SQLITE_MISUSE;
TimeStamp startTime = TimeStamp::Now();
int srv = ::sqlite3_exec(aNativeConnection, aSqlString, nullptr, nullptr,
nullptr);
// Report very slow SQL statements to Telemetry
TimeDuration duration = TimeStamp::Now() - startTime;
@@ -1246,16 +1264,20 @@ Connection::Close()
return internalClose(nativeConn);
}
NS_IMETHODIMP
Connection::AsyncClose(mozIStorageCompletionCallback *aCallback)
{
NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_SAME_THREAD);
+ // Check if AsyncClose or Close were already invoked.
+ if (!mDBConn) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
// The two relevant factors at this point are whether we have a database
// connection and whether we have an async execution thread. Here's what the
// states mean and how we handle them:
//
// - (mDBConn && asyncThread): The expected case where we are either an
// async connection or a sync connection that has been used asynchronously.
// Either way the caller must call us and not Close(). Nothing surprising
@@ -1329,19 +1351,17 @@ Connection::AsyncClose(mozIStorageComple
// off it, to pass it through the close procedure.
sqlite3 *nativeConn = mDBConn;
nsresult rv = setClosedState();
NS_ENSURE_SUCCESS(rv, rv);
// Create and dispatch our close event to the background thread.
nsCOMPtr<nsIRunnable> closeEvent = new AsyncCloseConnection(this,
nativeConn,
- completeEvent,
- mAsyncExecutionThread.forget());
-
+ completeEvent);
rv = asyncThread->Dispatch(closeEvent, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHODIMP
Connection::AsyncClone(bool aReadOnly,
@@ -1512,16 +1532,17 @@ Connection::GetDefaultPageSize(int32_t *
{
*_defaultPageSize = Service::getDefaultPageSize();
return NS_OK;
}
NS_IMETHODIMP
Connection::GetConnectionReady(bool *_ready)
{
+ MOZ_ASSERT(threadOpenedOn == NS_GetCurrentThread());
*_ready = connectionReady();
return NS_OK;
}
NS_IMETHODIMP
Connection::GetDatabaseFile(nsIFile **_dbFile)
{
if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
--- a/storage/mozStorageConnection.h
+++ b/storage/mozStorageConnection.h
@@ -99,16 +99,21 @@ public:
*
* @param aFileURL
* The nsIFileURL of the location of the database to open, or create if it
* does not exist.
*/
nsresult initialize(nsIFileURL *aFileURL);
/**
+ * Same as initialize, but to be used on the async thread.
+ */
+ nsresult initializeOnAsyncThread(nsIFile* aStorageFile);
+
+ /**
* Fetches runtime status information for this connection.
*
* @param aStatusOption One of the SQLITE_DBSTATUS options defined at
* http://www.sqlite.org/c3ref/c_dbstatus_options.html
* @param [optional] aMaxValue if provided, will be set to the highest
* istantaneous value.
* @return the current value for the specified option.
*/
@@ -134,19 +139,20 @@ public:
return mDBConn && static_cast<bool>(::sqlite3_get_autocommit(mDBConn));
};
/**
* Lazily creates and returns a background execution thread. In the future,
* the thread may be re-claimed if left idle, so you should call this
* method just before you dispatch and not save the reference.
*
- * This must be called from the main thread.
+ * This must be called from the opener thread.
*
- * @returns an event target suitable for asynchronous statement execution.
+ * @return an event target suitable for asynchronous statement execution.
+ * @note This method will return null once AsyncClose() has been called.
*/
nsIEventTarget *getAsyncExecutionTarget();
/**
* Mutex used by asynchronous statements to protect state. The mutex is
* declared on the connection object because there is no contention between
* asynchronous statements (they are serialized on mAsyncExecutionThread).
* Currently protects:
@@ -173,17 +179,17 @@ public:
/**
* Closes the SQLite database, and warns about any non-finalized statements.
*/
nsresult internalClose(sqlite3 *aDBConn);
/**
* Shuts down the passed-in async thread.
*/
- void shutdownAsyncThread(nsIThread *aAsyncThread);
+ void shutdownAsyncThread();
/**
* Obtains the filename of the connection. Useful for logging.
*/
nsCString getFilename();
/**
* Creates an sqlite3 prepared statement object from an SQL string.
@@ -219,33 +225,56 @@ public:
nsresult beginTransactionInternal(sqlite3 *aNativeConnection,
int32_t aTransactionType=TRANSACTION_DEFERRED);
nsresult commitTransactionInternal(sqlite3 *aNativeConnection);
nsresult rollbackTransactionInternal(sqlite3 *aNativeConnection);
bool connectionReady();
/**
- * True if this connection is shutting down but not yet closed.
+ * Thread-aware version of connectionReady, results per caller's thread are:
+ * - owner thread: Same as connectionReady(). True means we have a valid,
+ * un-closed database connection and it's not going away until you invoke
+ * Close() or AsyncClose().
+ * - async thread: Returns true at all times because you can't schedule
+ * runnables against the async thread after AsyncClose() has been called.
+ * Therefore, the connection is still around if your code is running.
+ * - any other thread: Race-prone Lies! If you are main-thread code in
+ * mozStorageService iterating over the list of connections, you need to
+ * acquire the sharedAsyncExecutionMutex for the connection, invoke
+ * connectionReady() while holding it, and then continue to hold it while
+ * you do whatever you need to do. This is because of off-main-thread
+ * consumers like dom/cache and IndexedDB and other QuotaManager clients.
+ */
+ bool isConnectionReadyOnThisThread();
+
+ /**
+ * True if this connection has inited shutdown.
*/
bool isClosing();
/**
* True if the underlying connection is closed.
* Any sqlite resources may be lost when this returns true, so nothing should
* try to use them.
+ * This locks on sharedAsyncExecutionMutex.
*/
bool isClosed();
/**
- * True if the async execution thread is alive and able to be used (i.e., it
- * is not in the process of shutting down.)
- *
- * This must be called from the main thread.
- */
+ * Same as isClosed(), but takes a proof-of-lock instead of locking internally.
+ */
+ bool isClosed(MutexAutoLock& lock);
+
+ /**
+ * True if the async execution thread is alive and able to be used (i.e., it
+ * is not in the process of shutting down.)
+ *
+ * This must be called from the opener thread.
+ */
bool isAsyncExecutionThreadAvailable();
nsresult initializeClone(Connection *aClone, bool aReadOnly);
private:
~Connection();
nsresult initializeInternal();
@@ -311,17 +340,17 @@ private:
*/
nsCString mTelemetryFilename;
/**
* Lazily created thread for asynchronous statement execution. Consumers
* should use getAsyncExecutionTarget rather than directly accessing this
* field.
*
- * This must be accessed only on the main thread.
+ * This must be modified only on the opener thread.
*/
nsCOMPtr<nsIThread> mAsyncExecutionThread;
/**
* Set to true by Close() or AsyncClose() prior to shutdown.
*
* If false, we guarantee both that the underlying sqlite3 database
* connection is still open and that getAsyncExecutionTarget() can
@@ -331,24 +360,16 @@ private:
* returns null.
*
* This variable should be accessed while holding the
* sharedAsyncExecutionMutex.
*/
bool mAsyncExecutionThreadShuttingDown;
/**
- * Tracks whether the async thread has been initialized and Shutdown() has
- * not yet been invoked on it.
- */
-#ifdef DEBUG
- bool mAsyncExecutionThreadIsAlive;
-#endif
-
- /**
* Set to true just prior to calling sqlite3_close on the
* connection.
*
* This variable should be accessed while holding the
* sharedAsyncExecutionMutex.
*/
bool mConnectionClosed;
--- a/storage/mozStorageService.cpp
+++ b/storage/mozStorageService.cpp
@@ -124,19 +124,21 @@ Service::CollectReports(nsIHandleReportC
{
nsTArray<RefPtr<Connection> > connections;
getConnections(connections);
for (uint32_t i = 0; i < connections.Length(); i++) {
RefPtr<Connection> &conn = connections[i];
// Someone may have closed the Connection, in which case we skip it.
- bool isReady;
- (void)conn->GetConnectionReady(&isReady);
- if (!isReady) {
+ // Note that we have consumers of the synchronous API that are off the
+ // main-thread, like the DOM Cache and IndexedDB, and as such we must be
+ // sure that we have a connection.
+ MutexAutoLock lockedAsyncScope(conn->sharedAsyncExecutionMutex);
+ if (!conn->connectionReady()) {
continue;
}
nsCString pathHead("explicit/storage/sqlite/");
// This filename isn't privacy-sensitive, and so is never anonymized.
pathHead.Append(conn->getFilename());
pathHead.Append('/');
@@ -343,16 +345,18 @@ Service::getConnections(/* inout */ nsTA
void
Service::minimizeMemory()
{
nsTArray<RefPtr<Connection> > connections;
getConnections(connections);
for (uint32_t i = 0; i < connections.Length(); i++) {
RefPtr<Connection> conn = connections[i];
+ // For non-main-thread owning/opening threads, we may be racing against them
+ // closing their connection or their thread. That's okay, see below.
if (!conn->connectionReady())
continue;
NS_NAMED_LITERAL_CSTRING(shrinkPragma, "PRAGMA shrink_memory");
nsCOMPtr<mozIStorageConnection> syncConn = do_QueryInterface(
NS_ISUPPORTS_CAST(mozIStorageAsyncConnection*, conn));
bool onOpenedThread = false;
@@ -371,20 +375,24 @@ Service::minimizeMemory()
conn->ExecuteSimpleSQLAsync(shrinkPragma, nullptr, getter_AddRefs(ps));
MOZ_ASSERT(NS_SUCCEEDED(rv), "Should have purged sqlite caches");
} else {
conn->ExecuteSimpleSQL(shrinkPragma);
}
} else {
// We are on the wrong thread, the query should be executed on the
// opener thread, so we must dispatch to it.
+ // It's possible the connection is already closed or will be closed by the
+ // time our runnable runs. ExecuteSimpleSQL will safely return with a
+ // failure in that case. If the thread is shutting down or shut down, the
+ // dispatch will fail and that's okay.
nsCOMPtr<nsIRunnable> event =
NewRunnableMethod<const nsCString>(
conn, &Connection::ExecuteSimpleSQL, shrinkPragma);
- conn->threadOpenedOn->Dispatch(event, NS_DISPATCH_NORMAL);
+ Unused << conn->threadOpenedOn->Dispatch(event, NS_DISPATCH_NORMAL);
}
}
}
void
Service::shutdown()
{
NS_IF_RELEASE(sXPConnect);
@@ -674,27 +682,18 @@ public:
, mCallback(aCallback)
{
MOZ_ASSERT(NS_IsMainThread());
}
NS_IMETHOD Run() override
{
MOZ_ASSERT(!NS_IsMainThread());
- nsresult rv = mStorageFile ? mConnection->initialize(mStorageFile)
- : mConnection->initialize();
+ nsresult rv = mConnection->initializeOnAsyncThread(mStorageFile);
if (NS_FAILED(rv)) {
- nsCOMPtr<nsIRunnable> closeRunnable =
- NewRunnableMethod<mozIStorageCompletionCallback*>(
- mConnection.get(),
- &Connection::AsyncClose,
- nullptr);
- MOZ_ASSERT(closeRunnable);
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(closeRunnable));
-
return DispatchResult(rv, nullptr);
}
if (mGrowthIncrement >= 0) {
// Ignore errors. In the future, we might wish to log them.
(void)mConnection->SetGrowthIncrement(mGrowthIncrement, EmptyCString());
}
@@ -933,27 +932,26 @@ Service::Observe(nsISupports *, const ch
nsCOMPtr<nsIObserverService> os =
mozilla::services::GetObserverService();
for (size_t i = 0; i < ArrayLength(sObserverTopics); ++i) {
(void)os->RemoveObserver(this, sObserverTopics[i]);
}
SpinEventLoopUntil([&]() -> bool {
- // We must wait until all connections are closed.
- nsTArray<RefPtr<Connection>> connections;
- getConnections(connections);
- for (auto& conn : connections) {
- if (conn->isClosing()) {
- return false;
- }
+ // We must wait until all the closing connections are closed.
+ nsTArray<RefPtr<Connection>> connections;
+ getConnections(connections);
+ for (auto& conn : connections) {
+ if (conn->isClosing()) {
+ return false;
}
-
- return true;
- });
+ }
+ return true;
+ });
if (gShutdownChecks == SCM_CRASH) {
nsTArray<RefPtr<Connection> > connections;
getConnections(connections);
for (uint32_t i = 0, n = connections.Length(); i < n; i++) {
if (!connections[i]->isClosed()) {
MOZ_CRASH();
}
--- a/storage/mozStorageStatement.cpp
+++ b/storage/mozStorageStatement.cpp
@@ -118,17 +118,18 @@ Statement::Statement()
}
nsresult
Statement::initialize(Connection *aDBConnection,
sqlite3 *aNativeConnection,
const nsACString &aSQLStatement)
{
MOZ_ASSERT(aDBConnection, "No database connection given!");
- MOZ_ASSERT(!aDBConnection->isClosed(), "Database connection should be valid");
+ MOZ_ASSERT(aDBConnection->isConnectionReadyOnThisThread(),
+ "Database connection should be valid");
MOZ_ASSERT(!mDBStatement, "Statement already initialized!");
MOZ_ASSERT(aNativeConnection, "No native connection given!");
int srv = aDBConnection->prepareStatement(aNativeConnection,
PromiseFlatCString(aSQLStatement),
&mDBStatement);
if (srv != SQLITE_OK) {
MOZ_LOG(gStorageLog, LogLevel::Error,
@@ -339,78 +340,53 @@ Statement::Finalize()
nsresult
Statement::internalFinalize(bool aDestructing)
{
if (!mDBStatement)
return NS_OK;
int srv = SQLITE_OK;
- if (!mDBConnection->isClosed()) {
- //
- // The connection is still open. While statement finalization and
- // closing may, in some cases, take place in two distinct threads,
- // we have a guarantee that the connection will remain open until
- // this method terminates:
- //
- // a. The connection will be closed synchronously. In this case,
- // there is no race condition, as everything takes place on the
- // same thread.
- //
- // b. The connection is closed asynchronously and this code is
- // executed on the opener thread. In this case, asyncClose() has
- // not been called yet and will not be called before we return
- // from this function.
- //
- // c. The connection is closed asynchronously and this code is
- // executed on the async execution thread. In this case,
- // AsyncCloseConnection::Run() has not been called yet and will
- // not be called before we return from this function.
- //
- // In either case, the connection is still valid, hence closing
- // here is safe.
- //
- MOZ_LOG(gStorageLog, LogLevel::Debug, ("Finalizing statement '%s' during garbage-collection",
- ::sqlite3_sql(mDBStatement)));
- srv = ::sqlite3_finalize(mDBStatement);
- }
+ {
+ // If the statement ends up being finalized twice, the second finalization
+ // would apply to a dangling pointer and may cause unexpected consequences.
+ // Thus we must be sure that the connection state won't change during this
+ // operation, to avoid racing with finalizations made by the closing
+ // connection. See Connection::internalClose().
+ MutexAutoLock lockedScope(mDBConnection->sharedAsyncExecutionMutex);
+ if (!mDBConnection->isClosed(lockedScope)) {
+ MOZ_LOG(gStorageLog, LogLevel::Debug, ("Finalizing statement '%s' during garbage-collection",
+ ::sqlite3_sql(mDBStatement)));
+ srv = ::sqlite3_finalize(mDBStatement);
+ }
#ifdef DEBUG
- else {
- //
- // The database connection is either closed or closing. The sqlite
- // statement has either been finalized already by the connection
- // or is about to be finalized by the connection.
- //
- // Finalizing it here would be useless and segfaultish.
- //
-
- SmprintfPointer msg = ::mozilla::Smprintf("SQL statement (%p) should have been finalized"
- " before garbage-collection. For more details on this statement, set"
- " NSPR_LOG_MESSAGES=mozStorage:5 .",
- mDBStatement);
+ else {
+ // The database connection is closed. The sqlite
+ // statement has either been finalized already by the connection
+ // or is about to be finalized by the connection.
+ //
+ // Finalizing it here would be useless and segfaultish.
+ //
+ // Note that we can't display the statement itself, as the data structure
+ // is not valid anymore. However, the address shown here should help
+ // developers correlate with the more complete debug message triggered
+ // by AsyncClose().
- //
- // Note that we can't display the statement itself, as the data structure
- // is not valid anymore. However, the address shown here should help
- // developers correlate with the more complete debug message triggered
- // by AsyncClose().
- //
+ SmprintfPointer msg = ::mozilla::Smprintf("SQL statement (%p) should have been finalized"
+ " before garbage-collection. For more details on this statement, set"
+ " NSPR_LOG_MESSAGES=mozStorage:5 .",
+ mDBStatement);
+ NS_WARNING(msg.get());
-#if 0
- // Deactivate the warning until we have fixed the exising culprit
- // (see bug 914070).
- NS_WARNING(msg.get());
-#endif // 0
-
- // Use %s so we aren't exposing random strings to printf interpolation.
- MOZ_LOG(gStorageLog, LogLevel::Warning, ("%s", msg.get()));
+ // Use %s so we aren't exposing random strings to printf interpolation.
+ MOZ_LOG(gStorageLog, LogLevel::Warning, ("%s", msg.get()));
+ }
+#endif // DEBUG
}
-#endif
-
mDBStatement = nullptr;
if (mAsyncStatement) {
// If the destructor called us, there are no pending async statements (they
// hold a reference to us) and we can/must just kill the statement directly.
if (aDestructing)
destructorAsyncFinalize();
else
@@ -588,17 +564,17 @@ Statement::ExecuteStep(bool *_moreResult
// Bind any parameters first before executing.
if (mParamsArray) {
// If we have more than one row of parameters to bind, they shouldn't be
// calling this method (and instead use executeAsync).
if (mParamsArray->length() != 1)
return NS_ERROR_UNEXPECTED;
BindingParamsArray::iterator row = mParamsArray->begin();
- nsCOMPtr<IStorageBindingParamsInternal> bindingInternal =
+ nsCOMPtr<IStorageBindingParamsInternal> bindingInternal =
do_QueryInterface(*row);
nsCOMPtr<mozIStorageError> error = bindingInternal->bind(mDBStatement);
if (error) {
int32_t srv;
(void)error->GetResult(&srv);
return convertResultCode(srv);
}
--- a/storage/test/unit/head_storage.js
+++ b/storage/test/unit/head_storage.js
@@ -3,16 +3,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cr = Components.results;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
do_get_profile();
var dirSvc = Cc["@mozilla.org/file/directory_service;1"].
getService(Ci.nsIProperties);
--- a/storage/test/unit/test_storage_connection.js
+++ b/storage/test/unit/test_storage_connection.js
@@ -231,41 +231,44 @@ add_task(function* test_asyncClose_succe
stmt.executeAsync();
stmt.finalize();
yield asyncClose(getOpenedDatabase());
// Reset gDBConn so that later tests will get a new connection object.
gDBConn = null;
});
-add_task(function* test_close_then_release_statement() {
- // Testing the behavior in presence of a bad client that finalizes
- // statements after the database has been closed (typically by
- // letting the gc finalize the statement).
- let db = getOpenedDatabase();
- let stmt = createStatement("SELECT * FROM test -- test_close_then_release_statement");
- db.close();
- stmt.finalize(); // Finalize too late - this should not crash
-
- // Reset gDBConn so that later tests will get a new connection object.
- gDBConn = null;
-});
+// Would assert on debug builds.
+if (!AppConstants.DEBUG) {
+ add_task(function* test_close_then_release_statement() {
+ // Testing the behavior in presence of a bad client that finalizes
+ // statements after the database has been closed (typically by
+ // letting the gc finalize the statement).
+ let db = getOpenedDatabase();
+ let stmt = createStatement("SELECT * FROM test -- test_close_then_release_statement");
+ db.close();
+ stmt.finalize(); // Finalize too late - this should not crash
-add_task(function* test_asyncClose_then_release_statement() {
- // Testing the behavior in presence of a bad client that finalizes
- // statements after the database has been async closed (typically by
- // letting the gc finalize the statement).
- let db = getOpenedDatabase();
- let stmt = createStatement("SELECT * FROM test -- test_asyncClose_then_release_statement");
- yield asyncClose(db);
- stmt.finalize(); // Finalize too late - this should not crash
+ // Reset gDBConn so that later tests will get a new connection object.
+ gDBConn = null;
+ });
- // Reset gDBConn so that later tests will get a new connection object.
- gDBConn = null;
-});
+ add_task(function* test_asyncClose_then_release_statement() {
+ // Testing the behavior in presence of a bad client that finalizes
+ // statements after the database has been async closed (typically by
+ // letting the gc finalize the statement).
+ let db = getOpenedDatabase();
+ let stmt = createStatement("SELECT * FROM test -- test_asyncClose_then_release_statement");
+ yield asyncClose(db);
+ stmt.finalize(); // Finalize too late - this should not crash
+
+ // Reset gDBConn so that later tests will get a new connection object.
+ gDBConn = null;
+ });
+}
add_task(function* test_close_fails_with_async_statement_ran() {
let deferred = Promise.defer();
let stmt = createStatement("SELECT * FROM test");
stmt.executeAsync();
stmt.finalize();
let db = getOpenedDatabase();
@@ -720,34 +723,40 @@ add_task(function* test_clone_attach_dat
let db = getService().openUnsharedDatabase(file);
conn.executeSimpleSQL(`ATTACH DATABASE '${db.databaseFile.path}' AS ${name}`);
db.close();
}
attachDB(db1, "attached_1");
attachDB(db1, "attached_2");
// These should not throw.
- db1.createStatement("SELECT * FROM attached_1.sqlite_master");
- db1.createStatement("SELECT * FROM attached_2.sqlite_master");
+ let stmt = db1.createStatement("SELECT * FROM attached_1.sqlite_master");
+ stmt.finalize();
+ stmt = db1.createStatement("SELECT * FROM attached_2.sqlite_master");
+ stmt.finalize();
// R/W clone.
let db2 = db1.clone();
do_check_true(db2.connectionReady);
// These should not throw.
- db2.createStatement("SELECT * FROM attached_1.sqlite_master");
- db2.createStatement("SELECT * FROM attached_2.sqlite_master");
+ stmt = db2.createStatement("SELECT * FROM attached_1.sqlite_master");
+ stmt.finalize();
+ stmt = db2.createStatement("SELECT * FROM attached_2.sqlite_master");
+ stmt.finalize();
// R/O clone.
let db3 = db1.clone(true);
do_check_true(db3.connectionReady);
// These should not throw.
- db3.createStatement("SELECT * FROM attached_1.sqlite_master");
- db3.createStatement("SELECT * FROM attached_2.sqlite_master");
+ stmt = db3.createStatement("SELECT * FROM attached_1.sqlite_master");
+ stmt.finalize();
+ stmt = db3.createStatement("SELECT * FROM attached_2.sqlite_master");
+ stmt.finalize();
db1.close();
db2.close();
db3.close();
});
add_task(function* test_getInterface() {
let db = getOpenedDatabase();