Skip to content

Commit

Permalink
Error code reporting (Android); check SQL error message
Browse files Browse the repository at this point in the history
  • Loading branch information
Christopher J. Brody committed Aug 30, 2016
1 parent 44ec47d commit edf3e32
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 35 deletions.
2 changes: 1 addition & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Changes

### cordova-sqlite-storage 1.4.8-pre1
### cordova-sqlite-storage 1.4.8-pre2

- Fix reporting of SQL processing errors in Windows version

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ In case you get stuck with something please read through the [support](#support)
- Memory issue observed when adding a large number of records due to the JSON implementation which is improved in [litehelpers / Cordova-sqlite-evcore-extbuild-free](https://github.com/litehelpers/Cordova-sqlite-evcore-extbuild-free) (available with GPL or commercial license options)
- Infinity (positive or negative) values are not supported (known to be broken on Android, may cause a crash on iOS ref: [litehelpers/Cordova-sqlite-storage#405](https://github.com/litehelpers/Cordova-sqlite-storage/issues/405))
- A stability issue was reported on the iOS version when in use together with [SockJS](http://sockjs.org/) client such as [pusher-js](https://github.com/pusher/pusher-js) at the same time (see [litehelpers/Cordova-sqlite-storage#196](https://github.com/litehelpers/Cordova-sqlite-storage/issues/196)). The workaround is to call sqlite functions and [SockJS](http://sockjs.org/) client functions in separate ticks (using setTimeout with 0 timeout).
- In case of an error, the error `code` member is bogus on Android and Windows (fixed for Android in [litehelpers / Cordova-sqlite-evplus-legacy-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-free), GPL or special commercial license options).
- In case of an error, the error `code` member is bogus on Windows (fixed for Android in [litehelpers / Cordova-sqlite-evplus-legacy-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-free), GPL or special commercial license options).
- Possible crash on Android when using Unicode emoji characters due to [Android bug 81341](https://code.google.com/p/android/issues/detail?id=81341), which *should* be fixed in Android 6.x
- Close/delete database bugs described below.
- When a database is opened and deleted without closing, the iOS version is known to leak resources.
Expand All @@ -330,7 +330,7 @@ Some more known issues are tracked in the [open Cordova-sqlite-storage bugs](htt
- This version will not work within a web worker (not properly supported by the Cordova framework). Use within a web worker is supported for Android and iOS in: [litehelpers / Cordova-sqlite-evplus-legacy-workers-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-workers-free) (available with GPL or premium commercial license options)
- In-memory database `db=window.sqlitePlugin.openDatabase({name: ':memory:', ...})` is currently not supported.
- The Android version cannot work with more than 100 open db files (due to the threading model used).
- _SQL error messages are not consistent between Android, iOS, and Windows versions._
- SQL error messages on Windows version are not consistent with Android and iOS versions.
- UNICODE `\u2028` (line separator) and `\u2029` (paragraph separator) characters are currently not supported and known to be broken in iOS version due to [Cordova bug CB-9435](https://issues.apache.org/jira/browse/CB-9435). There *may* be a similar issue with certain other UNICODE characters in the iOS version (needs further investigation). This is fixed in: [litehelpers / Cordova-sqlite-evplus-legacy-free](https://github.com/litehelpers/Cordova-sqlite-evplus-legacy-free) (available with GPL or special commercial license options)
- BLOB type is not supported in this version branch (*reading* of BLOBs is supported by [litehelpers / cordova-sqlite-ext](https://github.com/litehelpers/cordova-sqlite-ext) for Android/iOS)
- UNICODE `\u0000` (same as `\0`) character not working in Android (default Android-sqlite-connector database implentation) or Windows
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cordova-sqlite-storage",
"version": "1.4.8-pre1",
"version": "1.4.8-pre2",
"description": "Native interface to SQLite for PhoneGap/Cordova",
"cordova": {
"id": "cordova-sqlite-storage",
Expand Down
2 changes: 1 addition & 1 deletion plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0"
xmlns:android="http://schemas.android.com/apk/res/android"
id="cordova-sqlite-storage"
version="1.4.8-pre17">
version="1.4.8-pre2">

<name>Cordova sqlite storage plugin</name>

Expand Down
64 changes: 46 additions & 18 deletions spec/www/spec/misc-tx-legacy.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ var mytests = function() {
var scenarioName = scenarioList[i];
var suiteName = scenarioName + ': ';
var isWebSql = (i === 1);
var isOldImpl = (i === 2);
var isImpl2 = (i === 2);

// NOTE: MUST be defined in function scope, NOT outer scope:
var openDatabase = function(name, ignored1, ignored2, ignored3) {
if (isOldImpl) {
if (isImpl2) {
return window.sqlitePlugin.openDatabase({
// prevent reuse of database from default db implementation:
name: 'i2-'+name,
Expand Down Expand Up @@ -92,17 +92,26 @@ var mytests = function() {
start();
throw new Error('abort tx');
}, function(tx, error) {
ok(!!error, "valid error object");
expect(error).toBeDefined();

// XXX ONLY WORKING for iOS version of plugin:
if (isWebSql || !(isAndroid || isWindows || isWP8))
ok(!!error['code'], "valid error.code exists");
// XXX ONLY WORKING for Android/iOS platforms:
//if (isWebSql || !(isWindows || isWP8)) {
expect(error.code).toBeDefined();
expect(error.code).toBe(5);
//}

expect(error.message).toBeDefined();
//expect(error.message).toBe('--');
expect(error.message).toMatch(/near .*\"*\"*:*syntax error/);

// From built-in Android database exception message:
if (!isWebSql && isAndroid && isImpl2)
expect(error.message).toMatch(/near .*\"*\"*:*syntax error.*code 1/);

// SQLite error code part of Web SQL error.message:
if (isWebSql)
expect(error.message).toMatch(/1 near .*\"*\"*:*syntax error/);

ok(error.hasOwnProperty('message'), "error.message exists");
// XXX ONLY WORKING for iOS version of plugin:
if (isWebSql || !(isAndroid || isWindows || isWP8))
strictEqual(error.code, 5, "error.code === SQLException.SYNTAX_ERR (5)");
//equal(error.message, "Request failed: insert into test_table (data) VALUES ,123", "error.message");
start();

// We want this error to fail the entire transaction
Expand Down Expand Up @@ -135,15 +144,34 @@ var mytests = function() {
start();
throw new Error('abort tx');
}, function(tx, error) {
ok(!!error, "valid error object");
expect(error).toBeDefined();

// XXX ONLY WORKING for Android/iOS platforms:
//if (isWebSql || !(isWindows || isWP8)) {
expect(error.code).toBeDefined();
expect(error.code).toBe(6);
//}

expect(error.message).toBeDefined();
//expect(error.message).toBe('--');

if (isWebSql) // WebSQL may have a missing 'r' (iOS):
expect(error.message).toMatch(/constr?aint fail/);
else //if (!isWP8)
expect(error.message).toMatch(/constraint fail/);

// From built-in Android database exception message:
if (!isWebSql && isAndroid && isImpl2)
expect(error.message).toMatch(/not unique.*code 19/);

// SQLite error code part of Web SQL error.message (Android):
if (isWebSql && isAndroid)
expect(error.message).toMatch(/19 .*constraint fail/);

// XXX ONLY WORKING for iOS version of plugin:
if (isWebSql || !(isAndroid || isWindows || isWP8))
ok(!!error['code'], "valid error.code exists");
// SQLite error code part of Web SQL error.message (iOS):
if (isWebSql && !isAndroid)
expect(error.message).toMatch(/19 .*not unique/);

ok(error.hasOwnProperty('message'), "error.message exists");
//strictEqual(error.code, 6, "error.code === SQLException.CONSTRAINT_ERR (6)");
//equal(error.message, "Request failed: insert into test_table (data) VALUES (?),123", "error.message");
start();

// We want this error to fail the entire transaction
Expand Down
54 changes: 49 additions & 5 deletions src/android/io/sqlc/SQLiteAndroidDatabase.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

import android.database.Cursor;
import android.database.CursorWindow;

import android.database.sqlite.SQLiteConstraintException;
import android.database.sqlite.SQLiteCursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
Expand All @@ -19,8 +21,10 @@
import android.util.Log;

import java.io.File;

import java.lang.IllegalArgumentException;
import java.lang.Number;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand All @@ -47,6 +51,11 @@ class SQLiteAndroidDatabase
private static final Pattern DELETE_TABLE_NAME = Pattern.compile("^\\s*DELETE\\s+FROM\\s+(\\S+)",
Pattern.CASE_INSENSITIVE);

private static final Pattern SYNTAX_ERROR_PATTERN = Pattern.compile("^.*syntax.*error.*",
Pattern.CASE_INSENSITIVE);

private static final boolean isPostHoneycomb = android.os.Build.VERSION.SDK_INT >= 11;

File dbFile;

SQLiteDatabase mydb;
Expand Down Expand Up @@ -118,15 +127,17 @@ private void executeSqlBatchStatement(String query, JSONArray json_params, JSONA
boolean needRowsAffectedCompat = false;

JSONObject queryResult = null;

String errorMessage = "unknown";
int code = 0; // SQLException.UNKNOWN_ERR

try {
boolean needRawQuery = true;

QueryType queryType = getQueryType(query);

if (queryType == QueryType.update || queryType == queryType.delete) {
if (android.os.Build.VERSION.SDK_INT >= 11) {
if (isPostHoneycomb) {
SQLiteStatement myStatement = mydb.compileStatement(query);

if (json_params != null) {
Expand All @@ -136,10 +147,18 @@ private void executeSqlBatchStatement(String query, JSONArray json_params, JSONA
int rowsAffected = -1; // (assuming invalid)

// Use try & catch just in case android.os.Build.VERSION.SDK_INT >= 11 is lying:
// (Catch SQLiteException here to avoid extra retry)
try {
rowsAffected = myStatement.executeUpdateDelete();
// Indicate valid results:
needRawQuery = false;
} catch (SQLiteConstraintException ex) {
// Indicate problem & stop this query:
ex.printStackTrace();
errorMessage = "constraint failure: " + ex.getMessage();
code = 6; // SQLException.CONSTRAINT_ERR
Log.v("executeSqlBatch", "SQLiteStatement.executeUpdateDelete(): Error=" + errorMessage);
needRawQuery = false;
} catch (SQLiteException ex) {
// Indicate problem & stop this query:
ex.printStackTrace();
Expand All @@ -149,6 +168,8 @@ private void executeSqlBatchStatement(String query, JSONArray json_params, JSONA
} catch (Exception ex) {
// Assuming SDK_INT was lying & method not found:
// do nothing here & try again with raw query.
ex.printStackTrace();
Log.v("executeSqlBatch", "SQLiteStatement.executeUpdateDelete(): runtime error (fallback to old API): " + errorMessage);
}

// "finally" cleanup myStatement
Expand Down Expand Up @@ -187,9 +208,14 @@ private void executeSqlBatchStatement(String query, JSONArray json_params, JSONA
} else {
queryResult.put("rowsAffected", 0);
}
} catch (SQLiteConstraintException ex) {
// report constraint violation error result with the error message
ex.printStackTrace();
errorMessage = "constraint failure: " + ex.getMessage();
code = 6; // SQLException.CONSTRAINT_ERR
Log.v("executeSqlBatch", "SQLiteDatabase.executeInsert(): Error=" + errorMessage);
} catch (SQLiteException ex) {
// report error result with the error message
// could be constraint violation or some other error
// report some other error result with the error message
ex.printStackTrace();
errorMessage = ex.getMessage();
Log.v("executeSqlBatch", "SQLiteDatabase.executeInsert(): Error=" + errorMessage);
Expand Down Expand Up @@ -244,7 +270,21 @@ private void executeSqlBatchStatement(String query, JSONArray json_params, JSONA

// raw query for other statements:
if (needRawQuery) {
queryResult = this.executeSqlStatementQuery(mydb, query, json_params);
try {
queryResult = this.executeSqlStatementQuery(mydb, query, json_params);

} catch (SQLiteConstraintException ex) {
// report constraint violation error result with the error message
ex.printStackTrace();
errorMessage = "constraint failure: " + ex.getMessage();
code = 6; // SQLException.CONSTRAINT_ERR
Log.v("executeSqlBatch", "Raw query error=" + errorMessage);
} catch (SQLiteException ex) {
// report some other error result with the error message
ex.printStackTrace();
errorMessage = ex.getMessage();
Log.v("executeSqlBatch", "Raw query error=" + errorMessage);
}

if (needRowsAffectedCompat) {
queryResult.put("rowsAffected", rowsAffectedCompat);
Expand All @@ -265,11 +305,15 @@ private void executeSqlBatchStatement(String query, JSONArray json_params, JSONA

batchResults.put(r);
} else {
if (SYNTAX_ERROR_PATTERN.matcher(errorMessage).matches())
code = 5; // SQLException.SYNTAX_ERR

JSONObject r = new JSONObject();
r.put("type", "error");

JSONObject er = new JSONObject();
er.put("message", errorMessage);
er.put("code", code);
r.put("result", er);

batchResults.put(r);
Expand Down Expand Up @@ -415,7 +459,7 @@ private JSONObject executeSqlStatementQuery(SQLiteDatabase mydb, String query,
for (int i = 0; i < colCount; ++i) {
key = cur.getColumnName(i);

if (android.os.Build.VERSION.SDK_INT >= 11) {
if (isPostHoneycomb) {

// Use try & catch just in case android.os.Build.VERSION.SDK_INT >= 11 is lying:
try {
Expand Down
Loading

0 comments on commit edf3e32

Please sign in to comment.