Skip to content

Commit

Permalink
Fix transaction and statement errors to conform to SQLError.
Browse files Browse the repository at this point in the history
Sometimes the native layer returns errors as {message:string, code:number} and other times as just a string. This fix wraps key places to ensure that errors returned through the API always conform to SQLError per the API contract documented in the W3C spec.

In short, errors consistently have a message and code now and propagate from statement failure up to the transaction.

(@brodybits) from PR #170 thanks @aarononeal, adding www/SQLitePlugin.js as updated from SQLitePlugin.coffee.md
  • Loading branch information
aarononeal authored and Chris Brody committed Feb 28, 2015
1 parent 385d952 commit 3567f47
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 21 deletions.
43 changes: 33 additions & 10 deletions SQLitePlugin.coffee.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,29 @@
## utility functions:

# Errors returned to callbacks must conform to `SqlError` with a code and message.
# Some errors are of type `Error` or `string` and must be converted.
toSQLError = (error, code) ->
sqlError = error
code = 0 if !code # unknown by default
if !sqlError
sqlError = new Error "a plugin had an error but provided no response"
sqlError.code = code
if typeof sqlError is "string"
sqlError = new Error error
sqlError.code = code
if !sqlError.code && sqlError.message
sqlError.code = code
if !sqlError.code && !sqlError.message
sqlError = new Error "an unknown error was returned: " + JSON.stringify(sqlError)
sqlError.code = code
return sqlError
nextTick = window.setImmediate || (fun) ->
window.setTimeout(fun, 0)
return
Expand Down Expand Up @@ -87,14 +110,14 @@
SQLitePlugin::transaction = (fn, error, success) ->
if !@openDBs[@dbname]
error('database not open')
error(toSQLError('database not open'))
return
@addTransaction new SQLitePluginTransaction(this, fn, error, success, true, false)
return
SQLitePlugin::readTransaction = (fn, error, success) ->
if !@openDBs[@dbname]
error('database not open')
error(toSQLError('database not open'))
return
@addTransaction new SQLitePluginTransaction(this, fn, error, success, true, true)
return
Expand Down Expand Up @@ -174,7 +197,7 @@
if txlock
@executeSql "BEGIN", [], null, (tx, err) ->
throw new Error("unable to begin transaction: " + err.message)
throw toSQLError("unable to begin transaction: " + err.message, err.code)
return
Expand All @@ -189,7 +212,7 @@
txLocks[@db.dbname].inProgress = false
@db.startNextTransaction()
if @error
@error err
@error toSQLError(err)
return
SQLitePluginTransaction::executeSql = (sql, values, success, error) ->
Expand Down Expand Up @@ -232,9 +255,9 @@
SQLitePluginTransaction::handleStatementFailure = (handler, response) ->
if !handler
throw new Error "a statement with no error handler failed: " + response.message
throw toSQLError("a statement with no error handler failed: " + response.message, response.code)
if handler(this, response) isnt false
throw new Error "a statement error callback did not return false"
throw toSQLError("a statement error callback did not return false: " + response.message, response.code)
return
SQLitePluginTransaction::run = ->
Expand All @@ -252,9 +275,9 @@
if didSucceed
tx.handleStatementSuccess batchExecutes[index].success, response
else
tx.handleStatementFailure batchExecutes[index].error, response
tx.handleStatementFailure batchExecutes[index].error, toSQLError(response)
catch err
txFailure = err unless txFailure
txFailure = toSQLError(err) unless txFailure
if --waiting == 0
if txFailure
Expand Down Expand Up @@ -323,7 +346,7 @@
failed = (tx, err) ->
txLocks[tx.db.dbname].inProgress = false
tx.db.startNextTransaction()
if tx.error then tx.error new Error("error while trying to roll back: " + err.message)
if tx.error then tx.error toSQLError("error while trying to roll back: " + err.message, err.code)
return
@finalized = true
Expand All @@ -349,7 +372,7 @@
failed = (tx, err) ->
txLocks[tx.db.dbname].inProgress = false
tx.db.startNextTransaction()
if tx.error then tx.error new Error("error while trying to commit: " + err.message)
if tx.error then tx.error toSQLError("error while trying to commit: " + err.message, err.code)
return
@finalized = true
Expand Down
46 changes: 35 additions & 11 deletions www/SQLitePlugin.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,36 @@
(function() {
var READ_ONLY_REGEX, SQLiteFactory, SQLitePlugin, SQLitePluginTransaction, argsArray, dblocations, nextTick, root, txLocks;
var READ_ONLY_REGEX, SQLiteFactory, SQLitePlugin, SQLitePluginTransaction, argsArray, dblocations, nextTick, root, toSQLError, txLocks;

root = this;

READ_ONLY_REGEX = /^\s*(?:drop|delete|insert|update|create)\s/i;

txLocks = {};

toSQLError = function(error, code) {
var sqlError;
sqlError = error;
if (!code) {
code = 0;
}
if (!sqlError) {
sqlError = new Error("a plugin had an error but provided no response");
sqlError.code = code;
}
if (typeof sqlError === "string") {
sqlError = new Error(error);
sqlError.code = code;
}
if (!sqlError.code && sqlError.message) {
sqlError.code = code;
}
if (!sqlError.code && !sqlError.message) {
sqlError = new Error("an unknown error was returned: " + JSON.stringify(sqlError));
sqlError.code = code;
}
return sqlError;
};

nextTick = window.setImmediate || function(fun) {
window.setTimeout(fun, 0);
};
Expand Down Expand Up @@ -73,15 +97,15 @@

SQLitePlugin.prototype.transaction = function(fn, error, success) {
if (!this.openDBs[this.dbname]) {
error('database not open');
error(toSQLError('database not open'));
return;
}
this.addTransaction(new SQLitePluginTransaction(this, fn, error, success, true, false));
};

SQLitePlugin.prototype.readTransaction = function(fn, error, success) {
if (!this.openDBs[this.dbname]) {
error('database not open');
error(toSQLError('database not open'));
return;
}
this.addTransaction(new SQLitePluginTransaction(this, fn, error, success, true, true));
Expand Down Expand Up @@ -181,7 +205,7 @@
this.executes = [];
if (txlock) {
this.executeSql("BEGIN", [], null, function(tx, err) {
throw new Error("unable to begin transaction: " + err.message);
throw toSQLError("unable to begin transaction: " + err.message, err.code);
});
}
};
Expand All @@ -200,7 +224,7 @@
txLocks[this.db.dbname].inProgress = false;
this.db.startNextTransaction();
if (this.error) {
this.error(err);
this.error(toSQLError(err));
}
}
};
Expand Down Expand Up @@ -244,10 +268,10 @@

SQLitePluginTransaction.prototype.handleStatementFailure = function(handler, response) {
if (!handler) {
throw new Error("a statement with no error handler failed: " + response.message);
throw toSQLError("a statement with no error handler failed: " + response.message, response.code);
}
if (handler(this, response) !== false) {
throw new Error("a statement error callback did not return false");
throw toSQLError("a statement error callback did not return false: " + response.message, response.code);
}
};

Expand All @@ -266,12 +290,12 @@
if (didSucceed) {
tx.handleStatementSuccess(batchExecutes[index].success, response);
} else {
tx.handleStatementFailure(batchExecutes[index].error, response);
tx.handleStatementFailure(batchExecutes[index].error, toSQLError(response));
}
} catch (_error) {
err = _error;
if (!txFailure) {
txFailure = err;
txFailure = toSQLError(err);
}
}
if (--waiting === 0) {
Expand Down Expand Up @@ -348,7 +372,7 @@
txLocks[tx.db.dbname].inProgress = false;
tx.db.startNextTransaction();
if (tx.error) {
tx.error(new Error("error while trying to roll back: " + err.message));
tx.error(toSQLError("error while trying to roll back: " + err.message, err.code));
}
};
this.finalized = true;
Expand Down Expand Up @@ -377,7 +401,7 @@
txLocks[tx.db.dbname].inProgress = false;
tx.db.startNextTransaction();
if (tx.error) {
tx.error(new Error("error while trying to commit: " + err.message));
tx.error(toSQLError("error while trying to commit: " + err.message, err.code));
}
};
this.finalized = true;
Expand Down

0 comments on commit 3567f47

Please sign in to comment.