From 3567f475b1ea7794a9b44651c6fc6f2158d0ed64 Mon Sep 17 00:00:00 2001 From: Aaron Oneal Date: Tue, 30 Dec 2014 18:21:23 -0800 Subject: [PATCH] Fix transaction and statement errors to conform to SQLError. 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 --- SQLitePlugin.coffee.md | 43 ++++++++++++++++++++++++++++++--------- www/SQLitePlugin.js | 46 ++++++++++++++++++++++++++++++++---------- 2 files changed, 68 insertions(+), 21 deletions(-) diff --git a/SQLitePlugin.coffee.md b/SQLitePlugin.coffee.md index 586097117..9d1748d19 100644 --- a/SQLitePlugin.coffee.md +++ b/SQLitePlugin.coffee.md @@ -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 @@ -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 @@ -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 @@ -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) -> @@ -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 = -> @@ -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 @@ -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 @@ -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 diff --git a/www/SQLitePlugin.js b/www/SQLitePlugin.js index d8d7ddeae..9f23e6244 100644 --- a/www/SQLitePlugin.js +++ b/www/SQLitePlugin.js @@ -1,5 +1,5 @@ (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; @@ -7,6 +7,30 @@ 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); }; @@ -73,7 +97,7 @@ 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)); @@ -81,7 +105,7 @@ 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)); @@ -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); }); } }; @@ -200,7 +224,7 @@ txLocks[this.db.dbname].inProgress = false; this.db.startNextTransaction(); if (this.error) { - this.error(err); + this.error(toSQLError(err)); } } }; @@ -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); } }; @@ -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) { @@ -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; @@ -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;