Skip to content

Commit

Permalink
Include Web SQL code in transaction error object
Browse files Browse the repository at this point in the history
This updates the plugin so that it calls transaction error callbacks
with error objects that have a Web SQL 'code' property.

The plugin only maps a subset of the sqlite error codes to their Web
SQL counterparts.

Includes unit tests.
  • Loading branch information
lcsanchez committed Jun 3, 2013
1 parent 0910055 commit e25c6fb
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 10 deletions.
16 changes: 16 additions & 0 deletions src/ios/SQLitePlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@

#import "AppDelegate.h"

enum WebSQLError {
UNKNOWN_ERR = 0,
DATABASE_ERR = 1,
VERSION_ERR = 2,
TOO_LARGE_ERR = 3,
QUOTA_ERR = 4,
SYNTAX_ERR = 5,
CONSTRAINT_ERR = 6,
TIMEOUT_ERR = 7
};
typedef int WebSQLError;

@interface SQLitePlugin : CDVPlugin {
NSMutableDictionary *openDBs;
}
Expand All @@ -45,6 +57,10 @@

-(id) getDBPath:(id)dbFile;

+(NSDictionary *)captureSQLiteErrorFromDb:(sqlite3 *)db;

+(int)mapSQLiteErrorCode:(int)code;

// LIBB64
+(id) getBlobAsBase64String:(const char*) blob_chars
withlength: (int) blob_length;
Expand Down
61 changes: 53 additions & 8 deletions src/ios/SQLitePlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -245,19 +245,27 @@ -(void) executeSqlBatch: (CDVInvokedUrlCommand*)command
NSMutableDictionary *options = [command.arguments objectAtIndex:0];
NSMutableArray *results = [NSMutableArray arrayWithCapacity:0];
NSMutableArray *executes = [options objectForKey:@"executes"];
int status = CDVCommandStatus_OK;
CDVPluginResult* pluginResult;
NSDictionary *error = nil;

@synchronized(self) {
for (NSMutableDictionary *dict in executes) {
CDVPluginResult *result = [self executeSqlWithDict:dict];
if ([result.status intValue] == CDVCommandStatus_ERROR) {
status = CDVCommandStatus_ERROR;
error = [result message];
break;
}
[results addObject: result.message];
}
pluginResult = [CDVPluginResult resultWithStatus:status messageAsArray:results];

if (!error) {
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:results];
} else {
NSMutableDictionary *resultsAndError = [NSMutableDictionary dictionaryWithCapacity:2];
[resultsAndError setObject:results forKey:@"results"];
[resultsAndError setObject:error forKey:@"error"];
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:resultsAndError];
}
}

[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
Expand Down Expand Up @@ -301,7 +309,7 @@ -(CDVPluginResult*) executeSqlWithDict: (NSMutableDictionary*)options
sqlite3 *db = [dbPointer pointerValue];

const char *sql_stmt = [query UTF8String];
const char *errMsg = NULL;
NSDictionary *error = nil;
sqlite3_stmt *statement;
int result, i, column_type, count;
int previousRowsAffected, nowRowsAffected, diffRowsAffected;
Expand All @@ -322,7 +330,7 @@ -(CDVPluginResult*) executeSqlWithDict: (NSMutableDictionary*)options
previousInsertId = sqlite3_last_insert_rowid(db);

if (sqlite3_prepare_v2(db, sql_stmt, -1, &statement, NULL) != SQLITE_OK) {
errMsg = /* (char *) */ sqlite3_errmsg (db);
error = [SQLitePlugin captureSQLiteErrorFromDb:db];
keepGoing = NO;
} else {
for (int b = 1; b < query_parts.count; b++) {
Expand Down Expand Up @@ -393,15 +401,15 @@ -(CDVPluginResult*) executeSqlWithDict: (NSMutableDictionary*)options
break;

default:
errMsg = [[NSString stringWithFormat:@"sqlite3 error code %i", result] UTF8String];
error = [SQLitePlugin captureSQLiteErrorFromDb:db];
keepGoing = NO;
}
}

sqlite3_finalize (statement);

if (errMsg != NULL) {
return [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:[NSString stringWithFormat:@"SQL statement error : %s", errMsg]];
if (error) {
return [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:error];
}

[resultSet setObject:resultRows forKey:@"rows"];
Expand Down Expand Up @@ -433,6 +441,43 @@ -(void)dealloc
[super dealloc];
}

+(NSDictionary *)captureSQLiteErrorFromDb:(sqlite3 *)db
{
int code = sqlite3_errcode(db);
int webSQLCode = [SQLitePlugin mapSQLiteErrorCode:code];

NSMutableDictionary *error = [NSMutableDictionary dictionaryWithCapacity:4];

[error setObject:[NSNumber numberWithInt:webSQLCode] forKey:@"code"];

#if INCLUDE_SQLITE_ERROR_INFO
int extendedCode = sqlite3_extended_errcode(db);

This comment has been minimized.

Copy link
@brody4hire

brody4hire Aug 20, 2014

See #122

const char *message = sqlite3_errmsg(db);

[error setObject:[NSNumber numberWithInt:code] forKey:@"sqliteCode"];
[error setObject:[NSNumber numberWithInt:extendedCode] forKey:@"sqliteExtendedCode"];
[error setObject:[NSString stringWithUTF8String:message] forKey:@"sqliteMessage"];
#endif

return error;
}

+(int)mapSQLiteErrorCode:(int)code
{
// map the sqlite error code to
// the websql error code
switch(code) {
case SQLITE_ERROR:
return SYNTAX_ERR;
case SQLITE_FULL:
return QUOTA_ERR;
case SQLITE_CONSTRAINT:
return CONSTRAINT_ERR;
default:
return UNKNOWN_ERR;
}
}

+(id) getBlobAsBase64String:(const char*) blob_chars
withlength: (int) blob_length
{
Expand Down
63 changes: 63 additions & 0 deletions test-www/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,69 @@
});
});

var db;
module("Error codes", {
setup: function() {
stop();
db = window.sqlitePlugin.openDatabase("Database", "1.0", "Demo", -1);
db.transaction(function(tx) {
tx.executeSql('DROP TABLE IF EXISTS test_table');
tx.executeSql('CREATE TABLE IF NOT EXISTS test_table (data unique)');
}, function(error){
ok(false, error.message);
start();
}, function() {
start();
});
},
teardown: function() {
stop();
db.transaction(function(tx) {
tx.executeSql('DROP TABLE IF EXISTS test_table');
}, function(error){
ok(false, error.message);
start();
}, function() {
start();
});
}
});

test("constraint violation", function() {
stop();
db.transaction(function(tx) {
tx.executeSql("insert into test_table (data) VALUES (?)", [123], null, function(tx, error) {
ok(false, error.message);
start();
});

// This insertion will violate the unique constraint
tx.executeSql("insert into test_table (data) VALUES (?)", [123], function(tx, error) {
ok(false, error.message);
start();
}, function(tx, error) {
strictEqual(error.code, SQLException.CONSTRAINT_ERR, "error.code === CONSTRAINT_ERR");

This comment has been minimized.

Copy link
@brody4hire

brody4hire Aug 20, 2014

#121 the error code needs to be supported by the Android & WP(8) versions

equal(error.message, "Request failed: insert into test_table (data) VALUES (?),123", "error.message");
start();
});
});
});

test("syntax error", function() {
stop();
db.transaction(function(tx) {
// This insertion has a sql syntax error
tx.executeSql("insert into test_table (data) VALUES ", [123], function(tx, error) {
ok(false, error.message);
start();
}, function(tx, error) {
strictEqual(error.code, SQLException.SYNTAX_ERR, "error.code === SYNTAX_ERR");
equal(error.message, "Request failed: insert into test_table (data) VALUES ,123", "error.message");
start();
});
});
});

}

</script>
Expand Down
10 changes: 8 additions & 2 deletions www/SQLitePlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,13 +204,19 @@ if (!window.Cordova) window.Cordova = window.cordova;
});
}

var error = function (results) {
var error = function (resultsAndError) {
var results = resultsAndError.results;
var error = resultsAndError.error;
var j = 0;

for (; j < results.length; ++j) {
handleFor(j, true, results[j]);
}

for (; j < batchExecutes.length; ++j) {
handleFor(j, false, {message: 'Request failed: ' + opts[j].query});
error.message = 'Request failed: ' + opts[j].query;

handleFor(j, false, error);
}
};

Expand Down

0 comments on commit e25c6fb

Please sign in to comment.