Skip to content

Commit

Permalink
Fix executeSql to throw on finalized transactions.
Browse files Browse the repository at this point in the history
If executeSql is called on a finalized transaction it should throw. If it does not, timing errors get a lot harder to debug. ES6 promises, for example, generally execute on the next tick and then it becomes unclear why a statement fails to execute.
  • Loading branch information
aarononeal committed Jan 4, 2015
1 parent f688014 commit e6a9860
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 2 deletions.
15 changes: 13 additions & 2 deletions SQLitePlugin.coffee.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,17 @@ License for common Javascript: MIT or Apache
SQLitePluginTransaction::executeSql = (sql, values, success, error) ->
if @finalized
throw {message: 'InvalidStateError: DOM Exception 11: This transaction is already finalized. Transactions are committed after its success or failure handlers are called. If you are using a Promise to handle callbacks, be aware that implementations following the A+ standard adhere to run-to-completion semantics and so Promise resolution occurs on a subsequent tick and therefore after the transaction commits.', code: 11}
return
@_executeSqlInternal(sql, values, success, error)
return
# This method performs the actual execute but does not check for
# finalization since it is used to execute COMMIT and ROLLBACK.
SQLitePluginTransaction::_executeSqlInternal = (sql, values, success, error) ->
if @readOnly && READ_ONLY_REGEX.test(sql)
@handleStatementFailure(error, {message: 'invalid sql for a read-only transaction'})
return
Expand Down Expand Up @@ -352,7 +363,7 @@ License for common Javascript: MIT or Apache
@finalized = true
if @txlock
@executeSql "ROLLBACK", [], succeeded, failed
@_executeSqlInternal "ROLLBACK", [], succeeded, failed
@run()
else
succeeded(tx)
Expand All @@ -378,7 +389,7 @@ License for common Javascript: MIT or Apache
@finalized = true
if @txlock
@executeSql "COMMIT", [], succeeded, failed
@_executeSqlInternal "COMMIT", [], succeeded, failed
@run()
else
succeeded(tx)
Expand Down
32 changes: 32 additions & 0 deletions test-www/www/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,38 @@
});
});
});

test(suiteName + "executeSql fails outside transaction", function() {
withTestTable(function(db) {
expect(5);
ok(!!db, "db ok");
var txg;
stop(2);
db.transaction(function(tx) {
ok(!!tx, "tx ok");
txg = tx;
tx.executeSql("insert into test_table (data, data_num) VALUES (?,?)", ['test', null], function(tx, res) {
equal(res.rowsAffected, 1, 'row inserted');
});
start(1);
}, function(err) {
ok(false, err);
start(1);
}, function() {
// this simulates what would happen if a Promise ran on the next tick
// and invoked an execute on the transaction
try {
txg.executeSql("select count(*) as cnt from test_table", [], null, null);
ok(false, "executeSql should have thrown but continued instead");
} catch(err) {
ok(!!err.message, "error had valid message");
ok(/InvalidStateError|SQLTransaction/.test(err.message),
"execute must throw InvalidStateError; actual error: " + err.message);
}
start(1);
});
});
});

test(suiteName + "all columns should be included in result set (including 'null' columns)", function() {
withTestTable(function(db) {
Expand Down

0 comments on commit e6a9860

Please sign in to comment.