Skip to content

Commit

Permalink
Merge pull request #1 from minhaferzz/outsystems
Browse files Browse the repository at this point in the history
Outsystems
  • Loading branch information
minhaferzz authored Jan 13, 2022
2 parents dbe378d + be514e9 commit 0487c81
Show file tree
Hide file tree
Showing 7 changed files with 298 additions and 121 deletions.
12 changes: 12 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
# Changes
### cordova-sqlite-storage 3.2.0-OS3
- Update cordova-sqlite-storage-dependencies dependency to version 2.0.0-OS1 [RNMT-4515](https://outsystemsrd.atlassian.net/browse/RNMT-4515)
- Revert hotfix that was applied to ensure the support for Android 11 [RNMT-4515](https://outsystemsrd.atlassian.net/browse/RNMT-4515)

### cordova-sqlite-storage 3.2.0-OS2
- An hotfix was applied to ensure the support for Android 11 [RNMT-4515](https://outsystemsrd.atlassian.net/browse/RNMT-4515)

### cordova-sqlite-storage 3.2.0-OS1
- Added support for async calls

### cordova-sqlite-storage 3.2.0-OS
- Added support for multiple connections to the same database (enable some concurrency)

#### cordova-sqlite-storage 3.2.1-dev

Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
# OutSystems' version of SQLite cordova adapter

SQLite cordova interface based on *storage-master* branch of [litehelpers / Cordova-sqlite-storage](https://github.com/litehelpers/Cordova-sqlite-storage) customized to provide:

- Support for multiple transactions running concurrently (using multiple connections)

Below is an updated version of the source branch's README for further documentation, as of the last merge.

# Original README information

# Cross-platform SQLite storage plugin for Cordova/PhoneGap - cordova-sqlite-storage plugin version

Native SQLite component with API based on HTML5/[Web SQL (DRAFT) API](http://www.w3.org/TR/webdatabase/) for the following platforms:
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cordova-sqlite-storage",
"version": "3.2.1-dev",
"version": "3.2.0",
"description": "Native interface to SQLite for PhoneGap/Cordova - cordova-sqlite-storage plugin version",
"cordova": {
"id": "cordova-sqlite-storage",
Expand Down Expand Up @@ -30,7 +30,7 @@
},
"homepage": "https://github.com/xpbrew/cordova-sqlite-storage",
"dependencies": {
"cordova-sqlite-storage-dependencies": "2.0.0"
"cordova-sqlite-storage-dependencies": "https://github.com/OutSystems/cordova-sqlite-storage-dependencies#2.0.0-OS1"
},
"scripts": {
"clean-spec": "rm -rf spec/[mnp]* && git cl spec/config.xml && git st",
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="3.2.1-dev">
version="3.2.0">

<name>Cordova sqlite storage plugin - cordova-sqlite-storage plugin version</name>

Expand Down
87 changes: 51 additions & 36 deletions src/android/io/sqlc/SQLitePlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
public class SQLitePlugin extends CordovaPlugin {

/**
* Concurrent database runner map.
* Multiple database runner map (static).
*
* NOTE: no public static accessor to db (runner) map since it is not
* expected to work properly with db threading.
Expand All @@ -40,9 +40,9 @@ public class SQLitePlugin extends CordovaPlugin {
* https://gist.github.com/AlainODea/1375759b8720a3f9f094
*
* THANKS to @NeoLSN (Jason Yang/楊朝傑) for giving the pointer in:
* https://github.com/litehelpers/Cordova-sqlite-storage/issues/727
* https://github.com/litehelpers/Cordova-sqlite-storage/issues/727
*/
private Map<String, DBRunner> dbrmap = new ConcurrentHashMap<String, DBRunner>();
static Map<String, DBRunner> dbrmap = new ConcurrentHashMap<String, DBRunner>();

/**
* NOTE: Using default constructor, no explicit constructor.
Expand Down Expand Up @@ -101,9 +101,10 @@ private boolean executeAndPossiblyThrow(Action action, JSONArray args, CallbackC

case close:
o = args.getJSONObject(0);
dbname = o.getString("path");
dbname = o.getString("dbname");

// put request in the q to close the db
this.closeDatabase(dbname, cbc);
this.closeDatabase(getDBConnectionName(dbname, o), cbc);
break;

case delete:
Expand Down Expand Up @@ -136,7 +137,7 @@ private boolean executeAndPossiblyThrow(Action action, JSONArray args, CallbackC

// put db query in the queue to be executed in the db thread:
DBQuery q = new DBQuery(queries, jsonparams, cbc);
DBRunner r = dbrmap.get(dbname);
DBRunner r = dbrmap.get(getDBConnectionName(dbname, dbargs));
if (r != null) {
try {
r.q.put(q);
Expand All @@ -160,34 +161,38 @@ private boolean executeAndPossiblyThrow(Action action, JSONArray args, CallbackC
@Override
public void onDestroy() {
while (!dbrmap.isEmpty()) {
String dbname = dbrmap.keySet().iterator().next();
String dbConnectionName = dbrmap.keySet().iterator().next();

this.closeDatabaseNow(dbname);
this.closeDatabaseNow(dbConnectionName);

DBRunner r = dbrmap.get(dbname);
DBRunner r = dbrmap.get(dbConnectionName);
try {
// stop the db runner thread:
r.q.put(new DBQuery());
} catch(Exception e) {
Log.e(SQLitePlugin.class.getSimpleName(), "INTERNAL PLUGIN CLEANUP ERROR: could not stop db thread due to exception", e);
}
dbrmap.remove(dbname);
dbrmap.remove(dbConnectionName);
}
}

// --------------------------------------------------------------------------
// LOCAL METHODS
// --------------------------------------------------------------------------

private void startDatabase(String dbname, JSONObject options, CallbackContext cbc) {
DBRunner r = dbrmap.get(dbname);
private void startDatabase(String dbname, JSONObject options, CallbackContext cbc) throws JSONException {
String dbConnectionName = getDBConnectionName(dbname, options);

// TODO: is it an issue that we can orphan an existing thread? What should we do here?
// If we re-use the existing DBRunner it might be in the process of closing...
DBRunner r = dbrmap.get(dbConnectionName);

if (r != null) {
// NO LONGER EXPECTED due to BUG 666 workaround solution:
cbc.error("INTERNAL ERROR: database already open for db name: " + dbname);
} else {
r = new DBRunner(dbname, options, cbc);
dbrmap.put(dbname, r);
r = new DBRunner(dbname, dbConnectionName, options, cbc);
dbrmap.put(dbConnectionName, r);
this.cordova.getThreadPool().execute(r);
}
}
Expand Down Expand Up @@ -226,10 +231,10 @@ private SQLiteAndroidDatabase openDatabase(String dbname, CallbackContext cbc, b
/**
* Close a database (in another thread).
*
* @param dbName The name of the database file
* @param dbConnectionName The name of the database connection runner
*/
private void closeDatabase(String dbname, CallbackContext cbc) {
DBRunner r = dbrmap.get(dbname);
private void closeDatabase(String dbConnectionName, CallbackContext cbc) {
DBRunner r = dbrmap.get(dbConnectionName);
if (r != null) {
try {
r.q.put(new DBQuery(false, cbc));
Expand All @@ -249,10 +254,10 @@ private void closeDatabase(String dbname, CallbackContext cbc) {
/**
* Close a database (in the current thread).
*
* @param dbname The name of the database file
* @param dbConnectionName The name of the database connection runner
*/
private void closeDatabaseNow(String dbname) {
DBRunner r = dbrmap.get(dbname);
private void closeDatabaseNow(String dbConnectionName) {
DBRunner r = dbrmap.get(dbConnectionName);

if (r != null) {
SQLiteAndroidDatabase mydb = r.mydb;
Expand All @@ -263,7 +268,7 @@ private void closeDatabaseNow(String dbname) {
}

private void deleteDatabase(String dbname, CallbackContext cbc) {
DBRunner r = dbrmap.get(dbname);
DBRunner r = getRunnerForDb(dbname);
if (r != null) {
try {
r.q.put(new DBQuery(true, cbc));
Expand Down Expand Up @@ -300,9 +305,19 @@ private boolean deleteDatabaseNow(String dbname) {
return false;
}
}

private static String getDBConnectionName(String dbname, JSONObject options) {
String connectionName = options.optString("connectionName");
if (connectionName != null) {
return dbname + "_" + connectionName;
} else {
return dbname;
}
}

private class DBRunner implements Runnable {
final String dbname;
final String dbConnectionName;
private boolean oldImpl;
private boolean bugWorkaround;

Expand All @@ -311,8 +326,9 @@ private class DBRunner implements Runnable {

SQLiteAndroidDatabase mydb;

DBRunner(final String dbname, JSONObject options, CallbackContext cbc) {
DBRunner(final String dbname, final String dbConnectionName, JSONObject options, CallbackContext cbc) {
this.dbname = dbname;
this.dbConnectionName = dbConnectionName;
this.oldImpl = options.has("androidOldDatabaseImplementation");
Log.v(SQLitePlugin.class.getSimpleName(), "Android db implementation: built-in android.database.sqlite package");
this.bugWorkaround = this.oldImpl && options.has("androidBugWorkaround");
Expand All @@ -328,7 +344,7 @@ public void run() {
this.mydb = openDatabase(dbname, this.openCbc, this.oldImpl);
} catch (Exception e) {
Log.e(SQLitePlugin.class.getSimpleName(), "unexpected error, stopping db thread", e);
dbrmap.remove(dbname);
dbrmap.remove(dbConnectionName);
return;
}

Expand All @@ -351,24 +367,14 @@ public void run() {

if (dbq != null && dbq.close) {
try {
closeDatabaseNow(dbname);
closeDatabaseNow(dbConnectionName);

dbrmap.remove(dbname); // (should) remove ourself
dbrmap.remove(dbConnectionName); // (should) remove ourself

if (!dbq.delete) {
dbq.cbc.success();
} else {
try {
boolean deleteResult = deleteDatabaseNow(dbname);
if (deleteResult) {
dbq.cbc.success();
} else {
dbq.cbc.error("couldn't delete database");
}
} catch (Exception e) {
Log.e(SQLitePlugin.class.getSimpleName(), "couldn't delete database", e);
dbq.cbc.error("couldn't delete database: " + e);
}
deleteDatabase(dbname, dbq.cbc);
}
} catch (Exception e) {
Log.e(SQLitePlugin.class.getSimpleName(), "couldn't close database", e);
Expand All @@ -380,6 +386,15 @@ public void run() {
}
}

private DBRunner getRunnerForDb(String dbName) {
for (DBRunner runner : dbrmap.values()) {
if (runner.dbname.equals(dbName)) {
return runner;
}
}
return null;
}

private final class DBQuery {
// XXX TODO replace with DBRunner action enum:
final boolean stop;
Expand Down
50 changes: 43 additions & 7 deletions src/ios/SQLitePlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,8 @@ -(void)openNow: (CDVInvokedUrlCommand*)command
[self.commandDelegate sendPluginResult:pluginResult callbackId: command.callbackId];
return;
} else {
NSValue *dbPointer = [openDBs objectForKey:dbfilename];
NSString *dbConnectionName = [SQLitePlugin getDbConnectionNameForDb: dbfilename withOptions: options];
NSValue *dbPointer = [openDBs objectForKey: dbConnectionName];

if (dbPointer != NULL) {
// NO LONGER EXPECTED due to BUG 666 workaround solution:
Expand Down Expand Up @@ -162,7 +163,7 @@ -(void)openNow: (CDVInvokedUrlCommand*)command
// Attempt to read the SQLite master table [to support SQLCipher version]:
if(sqlite3_exec(db, (const char*)"SELECT count(*) FROM sqlite_master;", NULL, NULL, NULL) == SQLITE_OK) {
dbPointer = [NSValue valueWithPointer:db];
[openDBs setObject: dbPointer forKey: dbfilename];
[openDBs setObject: dbPointer forKey: dbConnectionName];
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:@"Database opened"];
} else {
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Unable to open DB with key"];
Expand All @@ -189,14 +190,15 @@ -(void)closeNow: (CDVInvokedUrlCommand*)command
CDVPluginResult* pluginResult = nil;
NSMutableDictionary *options = [command.arguments objectAtIndex:0];

NSString *dbFileName = [options objectForKey:@"path"];
NSString *dbFileName = [options objectForKey:@"dbname"];

if (dbFileName == NULL) {
// Should not happen:
DLog(@"No db name specified for close");
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"INTERNAL PLUGIN ERROR: You must specify database path"];
} else {
NSValue *val = [openDBs objectForKey:dbFileName];
NSString *dbConnectionName = [SQLitePlugin getDbConnectionNameForDb: dbFileName withOptions: options];
NSValue *val = [openDBs objectForKey:dbConnectionName];
sqlite3 *db = [val pointerValue];

if (db == NULL) {
Expand All @@ -207,7 +209,7 @@ -(void)closeNow: (CDVInvokedUrlCommand*)command
else {
DLog(@"close db name: %@", dbFileName);
sqlite3_close (db);
[openDBs removeObjectForKey:dbFileName];
[openDBs removeObjectForKey:dbConnectionName];
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:@"DB closed"];
}
}
Expand Down Expand Up @@ -242,7 +244,16 @@ -(void)deleteNow: (CDVInvokedUrlCommand*)command
if ([[NSFileManager defaultManager]fileExistsAtPath:dbPath]) {
DLog(@"delete full db path: %@", dbPath);
[[NSFileManager defaultManager]removeItemAtPath:dbPath error:nil];
[openDBs removeObjectForKey:dbFileName];

// Remove all connections associated to this DB
for (NSString *key in [openDBs allKeys]) {
NSValue *dbPointer = [openDBs objectForKey:key];
const char *obtainedPath = sqlite3_db_filename([dbPointer pointerValue], [@"main" UTF8String]);

if ([dbPath isEqualToString:[NSString stringWithUTF8String:obtainedPath]]) {
[openDBs removeObjectForKey:key];
}
}
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:@"DB deleted"];
} else {
DLog(@"delete: db was not found: %@", dbPath);
Expand Down Expand Up @@ -314,7 +325,8 @@ -(CDVPluginResult*) executeSqlWithDict: (NSMutableDictionary*)options andArgs: (

NSMutableArray *params = [options objectForKey:@"params"]; // optional

NSValue *dbPointer = [openDBs objectForKey:dbFileName];
NSString *dbConnectionName = [SQLitePlugin getDbConnectionNameForDb: dbFileName withOptions: dbargs];
NSValue *dbPointer = [openDBs objectForKey: dbConnectionName];
if (dbPointer == NULL) {
return [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"INTERNAL PLUGIN ERROR: No such database, you must open it first"];
}
Expand Down Expand Up @@ -527,4 +539,28 @@ +(int)mapSQLiteErrorCode:(int)code
}
}

+(NSString*)getDbConnectionNameForDb:(NSString*)dbName
withOptions:(NSMutableDictionary*)options
{
NSString *connectionName = [options objectForKey:@"connectionName"];
if (connectionName == NULL) {
return dbName;
} else {
return [[dbName stringByAppendingString:@"_"] stringByAppendingString:connectionName];
}
}

#ifdef READ_BLOB_AS_BASE64
+(NSString*)getBlobAsBase64String:(const char*)blob_chars
withLength:(int)blob_length
{
// THANKS for guidance: http://stackoverflow.com/a/8354941/1283667
NSData * data = [NSData dataWithBytes: (const void *)blob_chars length: blob_length];

// THANKS for guidance:
// https://github.com/apache/cordova-ios/blob/master/guides/API%20changes%20in%204.0.md#nsdatabase64h-removed
return [data base64EncodedStringWithOptions:0];
}
#endif

@end /* vim: set expandtab : */
Loading

0 comments on commit 0487c81

Please sign in to comment.