Bug 702559 - Adapting Sqlite.jsm to use mozIStorageAsyncConnection;r=mak

This commit is contained in:
David Rajchenbach-Teller 2013-06-27 09:01:00 -04:00
parent fed1e56a56
commit 4b9f52dd06
2 changed files with 75 additions and 61 deletions

View File

@ -26,7 +26,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "Task",
// Counts the number of created connections per database basename(). This is
// used for logging to distinguish connection instances.
let connectionCounters = {};
let connectionCounters = new Map();
/**
@ -89,34 +89,37 @@ function openConnection(options) {
}
let file = FileUtils.File(path);
let openDatabaseFn = sharedMemoryCache ?
Services.storage.openDatabase :
Services.storage.openUnsharedDatabase;
let basename = OS.Path.basename(path);
let number = connectionCounters.get(basename) || 0;
connectionCounters.set(basename, number + 1);
if (!connectionCounters[basename]) {
connectionCounters[basename] = 1;
}
let number = connectionCounters[basename]++;
let identifier = basename + "#" + number;
log.info("Opening database: " + path + " (" + identifier + ")");
try {
let connection = openDatabaseFn(file);
if (!connection.connectionReady) {
log.warn("Connection is not ready.");
return Promise.reject(new Error("Connection is not ready."));
}
return Promise.resolve(new OpenedConnection(connection, basename, number,
openedOptions));
} catch (ex) {
log.warn("Could not open database: " + CommonUtils.exceptionStr(ex));
return Promise.reject(ex);
let deferred = Promise.defer();
let options = null;
if (!sharedMemoryCache) {
options = Cc["@mozilla.org/hash-property-bag;1"].
createInstance(Ci.nsIWritablePropertyBag);
options.setProperty("shared", false);
}
Services.storage.openAsyncDatabase(file, options, function(status, connection) {
if (!connection) {
log.warn("Could not open connection: " + status);
deferred.reject(new Error("Could not open connection: " + status));
}
log.warn("Connection opened");
try {
deferred.resolve(
new OpenedConnection(connection.QueryInterface(Ci.mozIStorageAsyncConnection), basename, number,
openedOptions));
} catch (ex) {
log.warn("Could not open database: " + CommonUtils.exceptionStr(ex));
deferred.reject(ex);
}
});
return deferred.promise;
}
@ -225,51 +228,32 @@ OpenedConnection.prototype = Object.freeze({
TRANSACTION_TYPES: ["DEFERRED", "IMMEDIATE", "EXCLUSIVE"],
get connectionReady() {
return this._open && this._connection.connectionReady;
},
/**
* The row ID from the last INSERT operation.
*
* Because all statements are executed asynchronously, this could
* return unexpected results if multiple statements are performed in
* parallel. It is the caller's responsibility to schedule
* appropriately.
*
* It is recommended to only use this within transactions (which are
* handled as sequential statements via Tasks).
*/
get lastInsertRowID() {
this._ensureOpen();
return this._connection.lastInsertRowID;
},
/**
* The number of rows that were changed, inserted, or deleted by the
* last operation.
*
* The same caveats regarding asynchronous execution for
* `lastInsertRowID` also apply here.
*/
get affectedRows() {
this._ensureOpen();
return this._connection.affectedRows;
},
/**
* The integer schema version of the database.
*
* This is 0 if not schema version has been set.
*
* @return Promise<int>
*/
get schemaVersion() {
this._ensureOpen();
return this._connection.schemaVersion;
getSchemaVersion: function() {
let self = this;
return this.execute("PRAGMA user_version").then(
function onSuccess(result) {
if (result == null) {
return 0;
}
return JSON.stringify(result[0].getInt32(0));
}
);
},
set schemaVersion(value) {
setSchemaVersion: function(value) {
if (!Number.isInteger(value)) {
// Guarding against accidental SQLi
throw new TypeError("Schema version must be an integer. Got " + value);
}
this._ensureOpen();
this._connection.schemaVersion = value;
return this.execute("PRAGMA user_version = " + value);
},
/**

View File

@ -87,6 +87,34 @@ add_task(function test_get_dummy_database() {
yield db.close();
});
add_task(function test_schema_version() {
let db = yield getDummyDatabase("schema_version");
let version = yield db.getSchemaVersion();
do_check_eq(version, 0);
db.setSchemaVersion(14);
version = yield db.getSchemaVersion();
do_check_eq(version, 14);
for (let v of [0.5, "foobar", NaN]) {
let success;
try {
yield db.setSchemaVersion(v);
do_print("Schema version " + v + " should have been rejected");
success = false;
} catch (ex if ex.message.startsWith("Schema version must be an integer.")) {
success = true;
}
do_check_true(success);
version = yield db.getSchemaVersion();
do_check_eq(version, 14);
}
yield db.close();
});
add_task(function test_simple_insert() {
let c = yield getDummyDatabase("simple_insert");
@ -109,8 +137,10 @@ add_task(function test_simple_bound_object() {
let result = yield c.execute("INSERT INTO dirs VALUES (:id, :path)",
{id: 1, path: "foo"});
do_check_eq(result.length, 0);
do_check_eq(c.lastInsertRowID, 1);
do_check_eq(c.affectedRows, 1);
result = yield c.execute("SELECT id, path FROM dirs");
do_check_eq(result.length, 1);
do_check_eq(result[0].getResultByName("id"), 1);
do_check_eq(result[0].getResultByName("path"), "foo");
yield c.close();
});