Bug 833965 - Sqlite.jsm should support an array of binding parameters. r=gps

This commit is contained in:
Richard Newman 2013-04-10 17:57:42 -07:00
parent e93befdd15
commit 3fae00304f
2 changed files with 157 additions and 12 deletions

View File

@ -689,6 +689,50 @@ OpenedConnection.prototype = Object.freeze({
return count;
},
/**
* Helper method to bind parameters of various kinds through
* reflection.
*/
_bindParameters: function (statement, params) {
if (!params) {
return;
}
if (Array.isArray(params)) {
// It's an array of separate params.
if (params.length && (typeof(params[0]) == "object")) {
let paramsArray = statement.newBindingParamsArray();
for (let p of params) {
let bindings = paramsArray.newBindingParams();
for (let [key, value] of Iterator(p)) {
bindings.bindByName(key, value);
}
paramsArray.addParams(bindings);
}
statement.bindParameters(paramsArray);
return;
}
// Indexed params.
for (let i = 0; i < params.length; i++) {
statement.bindByIndex(i, params[i]);
}
return;
}
// Named params.
if (params && typeof(params) == "object") {
for (let k in params) {
statement.bindByName(k, params[k]);
}
return;
}
throw new Error("Invalid type for bound parameters. Expected Array or " +
"object. Got: " + params);
},
_executeStatement: function (sql, statement, params, onRow) {
if (statement.state != statement.MOZ_STORAGE_STATEMENT_READY) {
throw new Error("Statement is not ready for execution.");
@ -698,18 +742,7 @@ OpenedConnection.prototype = Object.freeze({
throw new Error("onRow must be a function. Got: " + onRow);
}
if (Array.isArray(params)) {
for (let i = 0; i < params.length; i++) {
statement.bindByIndex(i, params[i]);
}
} else if (params && typeof(params) == "object") {
for (let k in params) {
statement.bindByName(k, params[k]);
}
} else if (params) {
throw new Error("Invalid type for bound parameters. Expected Array or " +
"object. Got: " + params);
}
this._bindParameters(statement, params);
let index = this._statementCounter++;

View File

@ -575,6 +575,118 @@ add_task(function test_discard_cached() {
yield c.close();
});
add_task(function test_programmatic_binding() {
let c = yield getDummyDatabase("programmatic_binding");
let bindings = [
{id: 1, path: "foobar"},
{id: null, path: "baznoo"},
{id: 5, path: "toofoo"},
];
let sql = "INSERT INTO dirs VALUES (:id, :path)";
let result = yield c.execute(sql, bindings);
do_check_eq(result.length, 0);
let rows = yield c.executeCached("SELECT * from dirs");
do_check_eq(rows.length, 3);
yield c.close();
});
add_task(function test_programmatic_binding_transaction() {
let c = yield getDummyDatabase("programmatic_binding_transaction");
let bindings = [
{id: 1, path: "foobar"},
{id: null, path: "baznoo"},
{id: 5, path: "toofoo"},
];
let sql = "INSERT INTO dirs VALUES (:id, :path)";
yield c.executeTransaction(function transaction() {
let result = yield c.execute(sql, bindings);
do_check_eq(result.length, 0);
let rows = yield c.executeCached("SELECT * from dirs");
do_check_eq(rows.length, 3);
});
// Transaction committed.
let rows = yield c.executeCached("SELECT * from dirs");
do_check_eq(rows.length, 3);
yield c.close();
});
add_task(function test_programmatic_binding_transaction_partial_rollback() {
let c = yield getDummyDatabase("programmatic_binding_transaction_partial_rollback");
let bindings = [
{id: 2, path: "foobar"},
{id: 3, path: "toofoo"},
];
let sql = "INSERT INTO dirs VALUES (:id, :path)";
// Add some data in an implicit transaction before beginning the batch insert.
yield c.execute(sql, {id: 1, path: "works"});
let secondSucceeded = false;
try {
yield c.executeTransaction(function transaction() {
// Insert one row. This won't implicitly start a transaction.
let result = yield c.execute(sql, bindings[0]);
// Insert multiple rows. mozStorage will want to start a transaction.
// One of the inserts will fail, so the transaction should be rolled back.
let result = yield c.execute(sql, bindings);
secondSucceeded = true;
});
} catch (ex) {
print("Caught expected exception: " + ex);
}
// We did not get to the end of our in-transaction block.
do_check_false(secondSucceeded);
// Everything that happened in *our* transaction, not mozStorage's, got
// rolled back, but the first row still exists.
let rows = yield c.executeCached("SELECT * from dirs");
do_check_eq(rows.length, 1);
do_check_eq(rows[0].getResultByName("path"), "works");
yield c.close();
});
/**
* Just like the previous test, but relying on the implicit
* transaction established by mozStorage.
*/
add_task(function test_programmatic_binding_implicit_transaction() {
let c = yield getDummyDatabase("programmatic_binding_implicit_transaction");
let bindings = [
{id: 2, path: "foobar"},
{id: 1, path: "toofoo"},
];
let sql = "INSERT INTO dirs VALUES (:id, :path)";
let secondSucceeded = false;
yield c.execute(sql, {id: 1, path: "works"});
try {
let result = yield c.execute(sql, bindings);
secondSucceeded = true;
} catch (ex) {
print("Caught expected exception: " + ex);
}
do_check_false(secondSucceeded);
// The entire batch failed.
let rows = yield c.executeCached("SELECT * from dirs");
do_check_eq(rows.length, 1);
do_check_eq(rows[0].getResultByName("path"), "works");
yield c.close();
});
/**
* Test that direct binding of params and execution through mozStorage doesn't
* error when we manually create a transaction. See Bug 856925.