From 25f13a08ed0b6dfa64f8952d6adf3d89d1f163a9 Mon Sep 17 00:00:00 2001 From: Sergei Dryganets Date: Mon, 24 Jul 2017 01:12:13 -0700 Subject: [PATCH 1/4] plugin model for android initial implementation. Sqlitecipher plugin support added --- README.md | 14 +++- src/android/build.gradle | 28 ++----- src/android/settings.gradle | 4 +- src/android/sql-cipher-plugin/.gitignore | 1 + src/android/sql-cipher-plugin/build.gradle | 24 ++++++ .../sql-cipher-plugin/proguard-rules.pro | 25 +++++++ .../src/main/AndroidManifest.xml | 13 ++++ .../SqliteCipherConnectionProvider.java | 37 ++++++++++ .../sqlcipher/SqliteCipherCursor.java | 73 +++++++++++++++++++ .../sqlcipher/SqliteCipherDatabase.java | 60 +++++++++++++++ .../sqlcipher/SqliteCipherStatement.java | 50 +++++++++++++ src/android/sqlite-plugin-provider/.gitignore | 1 + .../sqlite-plugin-provider/build.gradle | 20 +++++ .../sqlite-plugin-provider/proguard-rules.pro | 25 +++++++ .../src/main/AndroidManifest.xml | 13 ++++ .../org/pgsqlite/sqlite/plugin/Database.java | 17 +++++ .../plugin/DatabaseConnectionProvider.java | 22 ++++++ .../plugin/DefaultConnectionProvider.java | 46 ++++++++++++ .../pgsqlite/sqlite/plugin/DefaultCursor.java | 71 ++++++++++++++++++ .../sqlite/plugin/DefaultDatabase.java | 56 ++++++++++++++ .../sqlite/plugin/DefaultStatement.java | 51 +++++++++++++ .../org/pgsqlite/sqlite/plugin/ICursor.java | 28 +++++++ .../pgsqlite/sqlite/plugin/SQLStatement.java | 20 +++++ .../src/main/res/values/strings.xml | 3 + src/android/storage/build.gradle | 26 +++++++ .../src/main/AndroidManifest.xml | 0 .../java/org/pgsqlite/CallbackContext.java | 0 .../main/java/org/pgsqlite/SQLiteArray.java | 0 .../main/java/org/pgsqlite/SQLiteObject.java | 0 .../main/java/org/pgsqlite/SQLitePlugin.java | 51 ++++++------- .../org/pgsqlite/SQLitePluginConverter.java | 0 .../org/pgsqlite/SQLitePluginPackage.java | 18 +++-- 32 files changed, 739 insertions(+), 58 deletions(-) create mode 100644 src/android/sql-cipher-plugin/.gitignore create mode 100644 src/android/sql-cipher-plugin/build.gradle create mode 100644 src/android/sql-cipher-plugin/proguard-rules.pro create mode 100644 src/android/sql-cipher-plugin/src/main/AndroidManifest.xml create mode 100644 src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherConnectionProvider.java create mode 100644 src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherCursor.java create mode 100644 src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherDatabase.java create mode 100644 src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherStatement.java create mode 100644 src/android/sqlite-plugin-provider/.gitignore create mode 100644 src/android/sqlite-plugin-provider/build.gradle create mode 100644 src/android/sqlite-plugin-provider/proguard-rules.pro create mode 100644 src/android/sqlite-plugin-provider/src/main/AndroidManifest.xml create mode 100644 src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/Database.java create mode 100644 src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DatabaseConnectionProvider.java create mode 100644 src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultConnectionProvider.java create mode 100644 src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultCursor.java create mode 100644 src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultDatabase.java create mode 100644 src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultStatement.java create mode 100644 src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/ICursor.java create mode 100644 src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/SQLStatement.java create mode 100644 src/android/sqlite-plugin-provider/src/main/res/values/strings.xml create mode 100644 src/android/storage/build.gradle rename src/android/{ => storage}/src/main/AndroidManifest.xml (100%) rename src/android/{ => storage}/src/main/java/org/pgsqlite/CallbackContext.java (100%) rename src/android/{ => storage}/src/main/java/org/pgsqlite/SQLiteArray.java (100%) rename src/android/{ => storage}/src/main/java/org/pgsqlite/SQLiteObject.java (100%) rename src/android/{ => storage}/src/main/java/org/pgsqlite/SQLitePlugin.java (96%) rename src/android/{ => storage}/src/main/java/org/pgsqlite/SQLitePluginConverter.java (100%) rename src/android/{ => storage}/src/main/java/org/pgsqlite/SQLitePluginPackage.java (73%) diff --git a/README.md b/README.md index 0bb48770..3f2c3ac0 100644 --- a/README.md +++ b/README.md @@ -230,7 +230,16 @@ npm install --save react-native-sqlite-storage ... include ':react-native-sqlite-storage' -project(':react-native-sqlite-storage').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sqlite-storage/src/android') +project(':react-native-sqlite-storage').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sqlite-storage/src/android/storage') + +include ':sqlite-plugin-provider' +project(':sqlite-plugin-provider').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sqlite-storage/src/android/sqlite-plugin-provider') + +// for sqlitecipher support: +include ':sql-cipher-plugin' +project(':sql-cipher-plugin').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sqlite-storage/src/android/sql-cipher-plugin') + + ``` #### Step 3 - Update app Gradle Build @@ -242,6 +251,9 @@ project(':react-native-sqlite-storage').projectDir = new File(rootProject.projec dependencies { ... compile project(':react-native-sqlite-storage') + compile project(':sqlite-plugin-provider') + // sqlite cipher support: + compile project(':sql-cipher-plugin') } ``` diff --git a/src/android/build.gradle b/src/android/build.gradle index e42f502a..c4705857 100644 --- a/src/android/build.gradle +++ b/src/android/build.gradle @@ -1,34 +1,16 @@ buildscript { repositories { + mavenLocal() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.3.0' + classpath 'com.android.tools.build:gradle:2.0.0' } } -apply plugin: 'com.android.library' - -android { - compileSdkVersion 23 - buildToolsVersion "23.0.1" - - defaultConfig { - minSdkVersion 16 - targetSdkVersion 22 - versionCode 1 - versionName "1.0" - } - lintOptions { - abortOnError false +allprojects { + repositories { + jcenter() } } - -repositories { - mavenCentral() -} - -dependencies { - compile 'com.facebook.react:react-native:0.14.+' -} diff --git a/src/android/settings.gradle b/src/android/settings.gradle index 74e11f66..66e94f43 100644 --- a/src/android/settings.gradle +++ b/src/android/settings.gradle @@ -1,2 +1,4 @@ rootProject.name = 'SQLitePlugin' -include ':app' \ No newline at end of file +include ':sqlite-plugin-provider' +include ':sql-cipher-plugin' +include ':storage' diff --git a/src/android/sql-cipher-plugin/.gitignore b/src/android/sql-cipher-plugin/.gitignore new file mode 100644 index 00000000..796b96d1 --- /dev/null +++ b/src/android/sql-cipher-plugin/.gitignore @@ -0,0 +1 @@ +/build diff --git a/src/android/sql-cipher-plugin/build.gradle b/src/android/sql-cipher-plugin/build.gradle new file mode 100644 index 00000000..975329d6 --- /dev/null +++ b/src/android/sql-cipher-plugin/build.gradle @@ -0,0 +1,24 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 25 + buildToolsVersion "25.0.0" + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 25 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile project(':sqlite-plugin-provider') + compile 'net.zetetic:android-database-sqlcipher:3.5.7@aar' +} diff --git a/src/android/sql-cipher-plugin/proguard-rules.pro b/src/android/sql-cipher-plugin/proguard-rules.pro new file mode 100644 index 00000000..10c22872 --- /dev/null +++ b/src/android/sql-cipher-plugin/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/sergeyd/Library/Android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/src/android/sql-cipher-plugin/src/main/AndroidManifest.xml b/src/android/sql-cipher-plugin/src/main/AndroidManifest.xml new file mode 100644 index 00000000..e9de8f58 --- /dev/null +++ b/src/android/sql-cipher-plugin/src/main/AndroidManifest.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherConnectionProvider.java b/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherConnectionProvider.java new file mode 100644 index 00000000..6608f6d6 --- /dev/null +++ b/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherConnectionProvider.java @@ -0,0 +1,37 @@ +package org.pgsqlite.sqlcipher; + +import android.content.Context; + +import org.pgsqlite.sqlite.plugin.Database; +import org.pgsqlite.sqlite.plugin.DatabaseConnectionProvider; + +import net.sqlcipher.DatabaseErrorHandler; +import net.sqlcipher.SQLException; +import net.sqlcipher.database.SQLiteDatabase; + +/** + * Written by Sergei Dryganets Jul 24/2017 + */ +public class SqliteCipherConnectionProvider implements DatabaseConnectionProvider { + private static volatile boolean sNativeLibraryLoaded; + @Override + public void init(Context context) { + if (!sNativeLibraryLoaded) { + sNativeLibraryLoaded = true; + SQLiteDatabase.loadLibs(context); + } + } + + @Override + public Database openDatabase(String databasePath, String password, int openFlags) { + return new SqliteCipherDatabase(SQLiteDatabase.openDatabase(databasePath, password, null, + openFlags, null, new DBErrorHandler())); + } + + private class DBErrorHandler implements DatabaseErrorHandler { + @Override + public void onCorruption(SQLiteDatabase dbObj) { + throw new SQLException("Database is corrupted"); + } + } +} diff --git a/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherCursor.java b/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherCursor.java new file mode 100644 index 00000000..7852b57e --- /dev/null +++ b/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherCursor.java @@ -0,0 +1,73 @@ +package org.pgsqlite.sqlcipher; + +import net.sqlcipher.Cursor; + +import org.pgsqlite.sqlite.plugin.ICursor; + +import java.io.IOException; + +/** + * Written by Sergei Dryganets Jul 24/2017 + */ +public class SqliteCipherCursor implements ICursor { + private final Cursor cursor; + + public SqliteCipherCursor(Cursor cursor) { + this.cursor = cursor; + } + + @Override + public boolean moveToFirst() { + return cursor.moveToFirst(); + } + + @Override + public boolean moveToNext() { + return cursor.moveToNext(); + } + + @Override + public int getColumnCount() { + return cursor.getColumnCount(); + } + + @Override + public String getColumnName(int i) { + return cursor.getColumnName(i); + } + + @Override + public int getType(int i) { + return cursor.getType(i); + } + + @Override + public long getLong(int i) { + return cursor.getLong(i); + } + + @Override + public double getDouble(int i) { + return cursor.getDouble(i); + } + + @Override + public String getString(int i) { + return cursor.getString(i); + } + + @Override + public byte[] getBlob(int i) { + return cursor.getBlob(i); + } + + @Override + public int getCount() { + return cursor.getCount(); + } + + @Override + public void close() throws IOException { + cursor.close(); + } +} diff --git a/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherDatabase.java b/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherDatabase.java new file mode 100644 index 00000000..9f427692 --- /dev/null +++ b/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherDatabase.java @@ -0,0 +1,60 @@ +package org.pgsqlite.sqlcipher; + +import net.sqlcipher.database.SQLiteDatabase; + +import org.pgsqlite.sqlite.plugin.Database; +import org.pgsqlite.sqlite.plugin.ICursor; +import org.pgsqlite.sqlite.plugin.SQLStatement; + +import java.io.IOException; + +/** + * Written by Sergei Dryganets Jul 24/2017 + */ +public class SqliteCipherDatabase implements Database { + private SQLiteDatabase database; + + SqliteCipherDatabase(SQLiteDatabase database) { + this.database = database; + } + + @Override + public void execSQL(String sql) { + database.execSQL(sql); + } + + @Override + public SQLStatement compileStatement(String sql) { + return new SqliteCipherStatement(database.compileStatement(sql)); + } + + @Override + public ICursor rawQuery(String sql, String[] params) { + return new SqliteCipherCursor(database.rawQuery(sql, params)); + } + + @Override + public boolean isOpen() { + return database.isOpen(); + } + + @Override + public void beginTransaction() { + database.beginTransaction(); + } + + @Override + public void setTransactionSuccessful() { + database.setTransactionSuccessful(); + } + + @Override + public void endTransaction() { + database.endTransaction(); + } + + @Override + public void close() throws IOException { + database.close(); + } +} diff --git a/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherStatement.java b/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherStatement.java new file mode 100644 index 00000000..34a87870 --- /dev/null +++ b/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherStatement.java @@ -0,0 +1,50 @@ +package org.pgsqlite.sqlcipher; + +import org.pgsqlite.sqlite.plugin.SQLStatement; + +import java.io.IOException; + +/** + * Written by Sergei Dryganets Jul 24/2017 + */ +public class SqliteCipherStatement implements SQLStatement { + private final net.sqlcipher.database.SQLiteStatement statement; + public SqliteCipherStatement(net.sqlcipher.database.SQLiteStatement statement) { + this.statement = statement; + } + + @Override + public void bindDouble(int i, double value) { + statement.bindDouble(i, value); + } + + @Override + public void bindString(int i, String value) { + statement.bindString(i, value); + } + + @Override + public void bindNull(int i) { + statement.bindNull(i); + } + + @Override + public void bindLong(int i, long value) { + statement.bindLong(i, value); + } + + @Override + public long executeInsert() { + return statement.executeInsert(); + } + + @Override + public int executeUpdateDelete() { + return statement.executeUpdateDelete(); + } + + @Override + public void close() throws IOException { + statement.close(); + } +} diff --git a/src/android/sqlite-plugin-provider/.gitignore b/src/android/sqlite-plugin-provider/.gitignore new file mode 100644 index 00000000..796b96d1 --- /dev/null +++ b/src/android/sqlite-plugin-provider/.gitignore @@ -0,0 +1 @@ +/build diff --git a/src/android/sqlite-plugin-provider/build.gradle b/src/android/sqlite-plugin-provider/build.gradle new file mode 100644 index 00000000..fb28c00a --- /dev/null +++ b/src/android/sqlite-plugin-provider/build.gradle @@ -0,0 +1,20 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 25 + buildToolsVersion "25.0.0" + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 25 + versionCode 1 + versionName "1.0" + + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} diff --git a/src/android/sqlite-plugin-provider/proguard-rules.pro b/src/android/sqlite-plugin-provider/proguard-rules.pro new file mode 100644 index 00000000..10c22872 --- /dev/null +++ b/src/android/sqlite-plugin-provider/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/sergeyd/Library/Android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/src/android/sqlite-plugin-provider/src/main/AndroidManifest.xml b/src/android/sqlite-plugin-provider/src/main/AndroidManifest.xml new file mode 100644 index 00000000..76853997 --- /dev/null +++ b/src/android/sqlite-plugin-provider/src/main/AndroidManifest.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/Database.java b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/Database.java new file mode 100644 index 00000000..42ee8921 --- /dev/null +++ b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/Database.java @@ -0,0 +1,17 @@ +package org.pgsqlite.sqlite.plugin; + +import java.io.Closeable; + +/** + * Written by Sergei Dryganets Jul 24/2017 + */ +public interface Database extends Closeable { + void execSQL(String sql); + SQLStatement compileStatement(String sql); + ICursor rawQuery(String sql, String[] params); + boolean isOpen(); + + void beginTransaction(); + void setTransactionSuccessful(); + void endTransaction(); +} diff --git a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DatabaseConnectionProvider.java b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DatabaseConnectionProvider.java new file mode 100644 index 00000000..366ef0b7 --- /dev/null +++ b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DatabaseConnectionProvider.java @@ -0,0 +1,22 @@ +package org.pgsqlite.sqlite.plugin; + +import android.content.Context; + +/** + * Written by Sergei Dryganets Jul 24/2017 + */ +public interface DatabaseConnectionProvider { + + int OPEN_READWRITE = 0x00000000; + int OPEN_READONLY = 0x00000001; + + /** + * Open flag: Flag for {@link #openDatabase} to create the database file if it does not + * already exist. + */ + int CREATE_IF_NECESSARY = 0x10000000; + + void init(Context context); + Database openDatabase(String databasePath, String password, int openFlags); +} + diff --git a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultConnectionProvider.java b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultConnectionProvider.java new file mode 100644 index 00000000..2ed7df30 --- /dev/null +++ b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultConnectionProvider.java @@ -0,0 +1,46 @@ +package org.pgsqlite.sqlite.plugin; + +import android.content.Context; +import android.database.DatabaseErrorHandler; +import android.database.SQLException; +import android.database.sqlite.SQLiteDatabase; + +/** + * Written by Sergei Dryganets Jul 24/2017 + */ +public class DefaultConnectionProvider implements DatabaseConnectionProvider { + /** + * Open flag: Flag for {@link #openDatabase} to open the database without support for + * localized collators. + * + * {@more} This causes the collator LOCALIZED not to be created. + * You must be consistent when using this flag to use the setting the database was + * created with. If this is set, {@link #setLocale} will do nothing. + */ + public static final int NO_LOCALIZED_COLLATORS = 0x00000010; + + /** + * Open flag: Flag for {@link #openDatabase} to open the database file with + * write-ahead logging enabled by default. + * Write-ahead logging cannot be used with read-only databases so the value of + * this flag is ignored if the database is opened read-only. + * + */ + public static final int ENABLE_WRITE_AHEAD_LOGGING = 0x20000000; + + @Override + public Database openDatabase(String databasePath, String password, int openFlags) { + return new DefaultDatabase(SQLiteDatabase.openDatabase(databasePath, null, openFlags, new DBErrorHandler())); + } + + @Override + public void init(Context context) { + } + + private class DBErrorHandler implements DatabaseErrorHandler { + @Override + public void onCorruption(SQLiteDatabase dbObj) { + throw new SQLException("Database is corrupted"); + } + } +} diff --git a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultCursor.java b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultCursor.java new file mode 100644 index 00000000..02caa59c --- /dev/null +++ b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultCursor.java @@ -0,0 +1,71 @@ +package org.pgsqlite.sqlite.plugin; + +import android.database.Cursor; + +import java.io.IOException; + +/** + * Written by Sergei Dryganets Jul 24/2017 + */ +public class DefaultCursor implements ICursor { + private final Cursor mCursor; + + public DefaultCursor(android.database.Cursor cursor) { + mCursor = cursor; + } + + @Override + public boolean moveToFirst() { + return mCursor.moveToFirst(); + } + + @Override + public boolean moveToNext() { + return mCursor.moveToNext(); + } + + @Override + public int getColumnCount() { + return mCursor.getColumnCount(); + } + + @Override + public String getColumnName(int i) { + return mCursor.getColumnName(i); + } + + @Override + public int getType(int i) { + return mCursor.getType(i); + } + + @Override + public long getLong(int i) { + return mCursor.getLong(i); + } + + @Override + public double getDouble(int i) { + return mCursor.getDouble(i); + } + + @Override + public String getString(int i) { + return mCursor.getString(i); + } + + @Override + public byte[] getBlob(int i) { + return mCursor.getBlob(i); + } + + @Override + public int getCount() { + return mCursor.getCount(); + } + + @Override + public void close() throws IOException { + mCursor.close(); + } +} diff --git a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultDatabase.java b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultDatabase.java new file mode 100644 index 00000000..7969d69f --- /dev/null +++ b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultDatabase.java @@ -0,0 +1,56 @@ +package org.pgsqlite.sqlite.plugin; + +import android.database.sqlite.SQLiteDatabase; + +import java.io.IOException; + +/** + * Written by Sergei Dryganets Jul 24/2017 + */ +public class DefaultDatabase implements Database { + private final SQLiteDatabase database; + + public DefaultDatabase(SQLiteDatabase database) { + this.database = database; + } + + @Override + public void execSQL(String sql) { + database.execSQL(sql); + } + + @Override + public SQLStatement compileStatement(String sql) { + return new DefaultStatement(database.compileStatement(sql)); + } + + @Override + public ICursor rawQuery(String sql, String[] params) { + return new DefaultCursor(database.rawQuery(sql, params)); + } + + @Override + public boolean isOpen() { + return database.isOpen(); + } + + @Override + public void beginTransaction() { + database.beginTransaction(); + } + + @Override + public void setTransactionSuccessful() { + database.setTransactionSuccessful(); + } + + @Override + public void endTransaction() { + database.endTransaction(); + } + + @Override + public void close() throws IOException { + database.close(); + } +} diff --git a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultStatement.java b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultStatement.java new file mode 100644 index 00000000..d9b748f3 --- /dev/null +++ b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultStatement.java @@ -0,0 +1,51 @@ +package org.pgsqlite.sqlite.plugin; + +import android.database.sqlite.SQLiteStatement; + +import java.io.IOException; + +/** + * Written by Sergei Dryganets Jul 24/2017 + */ +public class DefaultStatement implements SQLStatement { + private final SQLiteStatement mStatement; + + public DefaultStatement(SQLiteStatement statement) { + mStatement = statement; + } + + @Override + public void bindDouble(int i, double value) { + mStatement.bindDouble(i, value); + } + + @Override + public void bindString(int i, String value) { + mStatement.bindString(i, value); + } + + @Override + public void bindNull(int i) { + mStatement.bindNull(i); + } + + @Override + public void bindLong(int i, long value) { + mStatement.bindLong(i, value); + } + + @Override + public long executeInsert() { + return mStatement.executeInsert(); + } + + @Override + public int executeUpdateDelete() { + return mStatement.executeUpdateDelete(); + } + + @Override + public void close() throws IOException { + mStatement.close(); + } +} diff --git a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/ICursor.java b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/ICursor.java new file mode 100644 index 00000000..866b95d1 --- /dev/null +++ b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/ICursor.java @@ -0,0 +1,28 @@ +package org.pgsqlite.sqlite.plugin; + +import java.io.Closeable; + +/** + * Written by Sergei Dryganets Jul 24/2017 + */ +public interface ICursor extends Closeable { + boolean moveToFirst(); + + boolean moveToNext(); + + int getColumnCount(); + + String getColumnName(int i); + + int getType(int i); + + long getLong(int i); + + double getDouble(int i); + + String getString(int i); + + byte[] getBlob(int i); + + int getCount(); +} diff --git a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/SQLStatement.java b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/SQLStatement.java new file mode 100644 index 00000000..fcc0994d --- /dev/null +++ b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/SQLStatement.java @@ -0,0 +1,20 @@ +package org.pgsqlite.sqlite.plugin; + +import java.io.Closeable; + +/** + * Written by Sergei Dryganets Jul 24/2017 + */ +public interface SQLStatement extends Closeable { + void bindDouble(int i, double value); + + void bindString(int i, String value); + + void bindNull(int i); + + void bindLong(int i, long value); + + long executeInsert(); + + int executeUpdateDelete(); +} diff --git a/src/android/sqlite-plugin-provider/src/main/res/values/strings.xml b/src/android/sqlite-plugin-provider/src/main/res/values/strings.xml new file mode 100644 index 00000000..82b4713d --- /dev/null +++ b/src/android/sqlite-plugin-provider/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + SqlitePluginProvider + diff --git a/src/android/storage/build.gradle b/src/android/storage/build.gradle new file mode 100644 index 00000000..52a24fba --- /dev/null +++ b/src/android/storage/build.gradle @@ -0,0 +1,26 @@ + +apply plugin: 'com.android.library' + +android { + compileSdkVersion 25 + buildToolsVersion "25.0.0" + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 22 + versionCode 1 + versionName "1.0" + } + lintOptions { + abortOnError false + } +} + +repositories { + mavenCentral() +} + +dependencies { + compile 'com.facebook.react:react-native:0.14.+' + compile project(':sqlite-plugin-provider') +} diff --git a/src/android/src/main/AndroidManifest.xml b/src/android/storage/src/main/AndroidManifest.xml similarity index 100% rename from src/android/src/main/AndroidManifest.xml rename to src/android/storage/src/main/AndroidManifest.xml diff --git a/src/android/src/main/java/org/pgsqlite/CallbackContext.java b/src/android/storage/src/main/java/org/pgsqlite/CallbackContext.java similarity index 100% rename from src/android/src/main/java/org/pgsqlite/CallbackContext.java rename to src/android/storage/src/main/java/org/pgsqlite/CallbackContext.java diff --git a/src/android/src/main/java/org/pgsqlite/SQLiteArray.java b/src/android/storage/src/main/java/org/pgsqlite/SQLiteArray.java similarity index 100% rename from src/android/src/main/java/org/pgsqlite/SQLiteArray.java rename to src/android/storage/src/main/java/org/pgsqlite/SQLiteArray.java diff --git a/src/android/src/main/java/org/pgsqlite/SQLiteObject.java b/src/android/storage/src/main/java/org/pgsqlite/SQLiteObject.java similarity index 100% rename from src/android/src/main/java/org/pgsqlite/SQLiteObject.java rename to src/android/storage/src/main/java/org/pgsqlite/SQLiteObject.java diff --git a/src/android/src/main/java/org/pgsqlite/SQLitePlugin.java b/src/android/storage/src/main/java/org/pgsqlite/SQLitePlugin.java similarity index 96% rename from src/android/src/main/java/org/pgsqlite/SQLitePlugin.java rename to src/android/storage/src/main/java/org/pgsqlite/SQLitePlugin.java index 9988dfc9..a0306a7a 100755 --- a/src/android/src/main/java/org/pgsqlite/SQLitePlugin.java +++ b/src/android/storage/src/main/java/org/pgsqlite/SQLitePlugin.java @@ -8,11 +8,10 @@ package org.pgsqlite; import android.annotation.SuppressLint; +import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; -import android.database.sqlite.SQLiteStatement; -import android.content.Context; import android.util.Base64; import java.io.Closeable; @@ -39,6 +38,10 @@ import org.json.JSONException; import org.json.JSONArray; import org.json.JSONObject; +import org.pgsqlite.sqlite.plugin.Database; +import org.pgsqlite.sqlite.plugin.DatabaseConnectionProvider; +import org.pgsqlite.sqlite.plugin.ICursor; +import org.pgsqlite.sqlite.plugin.SQLStatement; import java.io.FileOutputStream; import java.io.InputStream; @@ -62,20 +65,20 @@ public class SQLitePlugin extends ReactContextBaseJavaModule { */ static ConcurrentHashMap dbrmap = new ConcurrentHashMap(); - /** - * Linked activity - */ - protected Context context = null; + private Context context = null; /** * Thread pool for database operations */ - protected ExecutorService threadPool; + private ExecutorService threadPool; - public SQLitePlugin(ReactApplicationContext reactContext) { + private DatabaseConnectionProvider provider; + + public SQLitePlugin(ReactApplicationContext reactContext, DatabaseConnectionProvider provider) { super(reactContext); this.context = reactContext.getApplicationContext(); this.threadPool = Executors.newCachedThreadPool(); + this.provider = provider; } /** @@ -365,11 +368,11 @@ private void startDatabase(String dbname, JSONObject options, CallbackContext cb * @return instance of SQLite database * @throws Exception */ - private SQLiteDatabase openDatabase(String dbname, String assetFilePath, int openFlags, CallbackContext cbc) throws Exception { + private Database openDatabase(String dbname, String assetFilePath, int openFlags, CallbackContext cbc) throws Exception { InputStream in = null; File dbfile = null; try { - SQLiteDatabase database = this.getDatabase(dbname); + Database database = this.getDatabase(dbname); if (database != null && database.isOpen()) { //this only happens when DBRunner is cycling the db for the locking work around. // otherwise, this should not happen - should be blocked at the execute("open") level @@ -415,7 +418,8 @@ private SQLiteDatabase openDatabase(String dbname, String assetFilePath, int ope FLog.v(TAG, "Opening sqlite db: " + dbfile.getAbsolutePath()); - SQLiteDatabase mydb = SQLiteDatabase.openDatabase(dbfile.getAbsolutePath(), null, openFlags); + // TODO implement password support + Database mydb = provider.openDatabase(dbfile.getAbsolutePath(), null, openFlags); if (cbc != null) // needed for Android locking/closing workaround cbc.success("database open"); @@ -498,11 +502,8 @@ private void closeDatabase(String dbName, CallbackContext cbc) { * @param dbName The name of the database file */ private void closeDatabaseNow(String dbName) { - SQLiteDatabase mydb = this.getDatabase(dbName); - - if (mydb != null) { - mydb.close(); - } + Database mydb = this.getDatabase(dbName); + closeQuietly(mydb); } /** @@ -576,7 +577,7 @@ private boolean deleteDatabaseNow(String dbname) { * * @param dbname The name of the database. */ - private SQLiteDatabase getDatabase(String dbname) { + private Database getDatabase(String dbname) { DBRunner r = dbrmap.get(dbname); return (r == null) ? null : r.mydb; } @@ -594,7 +595,7 @@ private SQLiteDatabase getDatabase(String dbname) { private void executeSqlBatch(String dbname, String[] queryarr, JSONArray[] jsonparams, String[] queryIDs, CallbackContext cbc) { - SQLiteDatabase mydb = getDatabase(dbname); + Database mydb = getDatabase(dbname); if (mydb == null) { // not allowed - can only happen if someone has closed (and possibly deleted) a database and then re-used the database @@ -621,7 +622,7 @@ private void executeSqlBatch(String dbname, String[] queryarr, JSONArray[] jsonp QueryType queryType = getQueryType(query); if (queryType == QueryType.update || queryType == QueryType.delete) { - SQLiteStatement myStatement = null; + SQLStatement myStatement = null; int rowsAffected = -1; // (assuming invalid) try { @@ -652,7 +653,7 @@ private void executeSqlBatch(String dbname, String[] queryarr, JSONArray[] jsonp if (queryType == QueryType.insert && jsonparams != null) { needRawQuery = false; - SQLiteStatement myStatement = mydb.compileStatement(query); + SQLStatement myStatement = mydb.compileStatement(query); bindArgsToStatement(myStatement, jsonparams[i]); @@ -768,7 +769,7 @@ private QueryType getQueryType(String query) { return QueryType.other; } - private void bindArgsToStatement(SQLiteStatement myStatement, JSONArray sqlArgs) throws JSONException { + private void bindArgsToStatement(SQLStatement myStatement, JSONArray sqlArgs) throws JSONException { for (int i = 0; i < sqlArgs.length(); i++) { if (sqlArgs.get(i) instanceof Float || sqlArgs.get(i) instanceof Double) { myStatement.bindDouble(i + 1, sqlArgs.getDouble(i)); @@ -792,12 +793,12 @@ private void bindArgsToStatement(SQLiteStatement myStatement, JSONArray sqlArgs) * * @return results in string form */ - private JSONObject executeSqlStatementQuery(SQLiteDatabase mydb, + private JSONObject executeSqlStatementQuery(Database mydb, String query, JSONArray paramsAsJson, CallbackContext cbc) throws Exception { JSONObject rowsResult = new JSONObject(); - Cursor cur = null; + ICursor cur = null; try { try { String[] params; @@ -854,7 +855,7 @@ private JSONObject executeSqlStatementQuery(SQLiteDatabase mydb, } @SuppressLint("NewApi") - private void bindRow(JSONObject row, String key, Cursor cur, int i) throws JSONException { + private void bindRow(JSONObject row, String key, ICursor cur, int i) throws JSONException { int curType = cur.getType(i); switch (curType) { @@ -895,7 +896,7 @@ private class DBRunner implements Runnable { final BlockingQueue q; final CallbackContext openCbc; - SQLiteDatabase mydb; + Database mydb; DBRunner(final String dbname, JSONObject options, CallbackContext cbc) { this.dbname = dbname; diff --git a/src/android/src/main/java/org/pgsqlite/SQLitePluginConverter.java b/src/android/storage/src/main/java/org/pgsqlite/SQLitePluginConverter.java similarity index 100% rename from src/android/src/main/java/org/pgsqlite/SQLitePluginConverter.java rename to src/android/storage/src/main/java/org/pgsqlite/SQLitePluginConverter.java diff --git a/src/android/src/main/java/org/pgsqlite/SQLitePluginPackage.java b/src/android/storage/src/main/java/org/pgsqlite/SQLitePluginPackage.java similarity index 73% rename from src/android/src/main/java/org/pgsqlite/SQLitePluginPackage.java rename to src/android/storage/src/main/java/org/pgsqlite/SQLitePluginPackage.java index be250278..90e3a6ef 100644 --- a/src/android/src/main/java/org/pgsqlite/SQLitePluginPackage.java +++ b/src/android/storage/src/main/java/org/pgsqlite/SQLitePluginPackage.java @@ -13,6 +13,9 @@ import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.uimanager.ViewManager; +import org.pgsqlite.sqlite.plugin.DatabaseConnectionProvider; +import org.pgsqlite.sqlite.plugin.DefaultConnectionProvider; + import java.util.ArrayList; import java.util.Collections; @@ -20,15 +23,14 @@ public class SQLitePluginPackage implements ReactPackage { - /** - * @deprecated, use method without activity - * activity parameter is ignored - */ - public SQLitePluginPackage(Activity activity){ - this(); - } + private final DatabaseConnectionProvider mProvider; public SQLitePluginPackage() { + this(new DefaultConnectionProvider()); + } + + public SQLitePluginPackage(DatabaseConnectionProvider provider) { + mProvider = provider; } @Override @@ -36,7 +38,7 @@ public List createNativeModules( ReactApplicationContext reactContext) { List modules = new ArrayList<>(); - modules.add(new SQLitePlugin(reactContext)); + modules.add(new SQLitePlugin(reactContext, mProvider)); return modules; } From e5fd3a7955c3cd016680996b5795b495eab46fe2 Mon Sep 17 00:00:00 2001 From: Sergei Dryganets Date: Sun, 30 Jul 2017 20:56:36 -0700 Subject: [PATCH 2/4] Added password support to android plugin. If password provided and SQLiteCihperProvider selected database will be encripted --- .../SqliteCipherConnectionProvider.java | 4 +- .../sqlcipher/SqliteCipherCursor.java | 10 ++-- .../sqlcipher/SqliteCipherDatabase.java | 4 +- .../org/pgsqlite/sqlite/plugin/Cursor.java | 48 ++++++++++++++++ .../org/pgsqlite/sqlite/plugin/Database.java | 2 +- .../plugin/DatabaseConnectionProvider.java | 20 ++++++- .../plugin/DefaultConnectionProvider.java | 22 -------- .../pgsqlite/sqlite/plugin/DefaultCursor.java | 6 +- .../sqlite/plugin/DefaultDatabase.java | 2 +- .../org/pgsqlite/sqlite/plugin/ICursor.java | 28 ---------- .../main/java/org/pgsqlite/SQLitePlugin.java | 56 ++++++++++--------- 11 files changed, 110 insertions(+), 92 deletions(-) create mode 100644 src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/Cursor.java delete mode 100644 src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/ICursor.java diff --git a/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherConnectionProvider.java b/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherConnectionProvider.java index 6608f6d6..4900727b 100644 --- a/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherConnectionProvider.java +++ b/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherConnectionProvider.java @@ -14,8 +14,8 @@ */ public class SqliteCipherConnectionProvider implements DatabaseConnectionProvider { private static volatile boolean sNativeLibraryLoaded; - @Override - public void init(Context context) { + + public SqliteCipherConnectionProvider(Context context) { if (!sNativeLibraryLoaded) { sNativeLibraryLoaded = true; SQLiteDatabase.loadLibs(context); diff --git a/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherCursor.java b/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherCursor.java index 7852b57e..698cac9c 100644 --- a/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherCursor.java +++ b/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherCursor.java @@ -1,18 +1,16 @@ package org.pgsqlite.sqlcipher; -import net.sqlcipher.Cursor; - -import org.pgsqlite.sqlite.plugin.ICursor; +import org.pgsqlite.sqlite.plugin.Cursor; import java.io.IOException; /** * Written by Sergei Dryganets Jul 24/2017 */ -public class SqliteCipherCursor implements ICursor { - private final Cursor cursor; +public class SqliteCipherCursor implements Cursor { + private final net.sqlcipher.Cursor cursor; - public SqliteCipherCursor(Cursor cursor) { + public SqliteCipherCursor(net.sqlcipher.Cursor cursor) { this.cursor = cursor; } diff --git a/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherDatabase.java b/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherDatabase.java index 9f427692..3e083b6d 100644 --- a/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherDatabase.java +++ b/src/android/sql-cipher-plugin/src/main/java/org/pgsqlite/sqlcipher/SqliteCipherDatabase.java @@ -3,7 +3,7 @@ import net.sqlcipher.database.SQLiteDatabase; import org.pgsqlite.sqlite.plugin.Database; -import org.pgsqlite.sqlite.plugin.ICursor; +import org.pgsqlite.sqlite.plugin.Cursor; import org.pgsqlite.sqlite.plugin.SQLStatement; import java.io.IOException; @@ -29,7 +29,7 @@ public SQLStatement compileStatement(String sql) { } @Override - public ICursor rawQuery(String sql, String[] params) { + public Cursor rawQuery(String sql, String[] params) { return new SqliteCipherCursor(database.rawQuery(sql, params)); } diff --git a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/Cursor.java b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/Cursor.java new file mode 100644 index 00000000..3fca06a2 --- /dev/null +++ b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/Cursor.java @@ -0,0 +1,48 @@ +package org.pgsqlite.sqlite.plugin; + +import java.io.Closeable; + +/** + * Written by Sergei Dryganets Jul 24/2017 + */ +public interface Cursor extends Closeable { + + /* + * Values returned by {@link #getType(int)}. + * These should be consistent with the corresponding types defined in CursorWindow.h + */ + /** Value returned by {@link #getType(int)} if the specified column is null */ + int FIELD_TYPE_NULL = 0; + + /** Value returned by {@link #getType(int)} if the specified column type is integer */ + int FIELD_TYPE_INTEGER = 1; + + /** Value returned by {@link #getType(int)} if the specified column type is float */ + int FIELD_TYPE_FLOAT = 2; + + /** Value returned by {@link #getType(int)} if the specified column type is string */ + int FIELD_TYPE_STRING = 3; + + /** Value returned by {@link #getType(int)} if the specified column type is blob */ + int FIELD_TYPE_BLOB = 4; + + boolean moveToFirst(); + + boolean moveToNext(); + + int getColumnCount(); + + String getColumnName(int i); + + int getType(int i); + + long getLong(int i); + + double getDouble(int i); + + String getString(int i); + + byte[] getBlob(int i); + + int getCount(); +} diff --git a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/Database.java b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/Database.java index 42ee8921..e3732b9e 100644 --- a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/Database.java +++ b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/Database.java @@ -8,7 +8,7 @@ public interface Database extends Closeable { void execSQL(String sql); SQLStatement compileStatement(String sql); - ICursor rawQuery(String sql, String[] params); + Cursor rawQuery(String sql, String[] params); boolean isOpen(); void beginTransaction(); diff --git a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DatabaseConnectionProvider.java b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DatabaseConnectionProvider.java index 366ef0b7..5310feba 100644 --- a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DatabaseConnectionProvider.java +++ b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DatabaseConnectionProvider.java @@ -10,13 +10,31 @@ public interface DatabaseConnectionProvider { int OPEN_READWRITE = 0x00000000; int OPEN_READONLY = 0x00000001; + /** + * Open flag: Flag for {@link #openDatabase} to open the database without support for + * localized collators. + * + * {@more} This causes the collator LOCALIZED not to be created. + * You must be consistent when using this flag to use the setting the database was + * created with. If this is set, {@link #setLocale} will do nothing. + */ + int NO_LOCALIZED_COLLATORS = 0x00000010; + /** * Open flag: Flag for {@link #openDatabase} to create the database file if it does not * already exist. */ int CREATE_IF_NECESSARY = 0x10000000; - void init(Context context); + /** + * Open flag: Flag for {@link #openDatabase} to open the database file with + * write-ahead logging enabled by default. + * Write-ahead logging cannot be used with read-only databases so the value of + * this flag is ignored if the database is opened read-only. + * + */ + int ENABLE_WRITE_AHEAD_LOGGING = 0x20000000; + Database openDatabase(String databasePath, String password, int openFlags); } diff --git a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultConnectionProvider.java b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultConnectionProvider.java index 2ed7df30..3d63ca94 100644 --- a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultConnectionProvider.java +++ b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultConnectionProvider.java @@ -9,34 +9,12 @@ * Written by Sergei Dryganets Jul 24/2017 */ public class DefaultConnectionProvider implements DatabaseConnectionProvider { - /** - * Open flag: Flag for {@link #openDatabase} to open the database without support for - * localized collators. - * - * {@more} This causes the collator LOCALIZED not to be created. - * You must be consistent when using this flag to use the setting the database was - * created with. If this is set, {@link #setLocale} will do nothing. - */ - public static final int NO_LOCALIZED_COLLATORS = 0x00000010; - - /** - * Open flag: Flag for {@link #openDatabase} to open the database file with - * write-ahead logging enabled by default. - * Write-ahead logging cannot be used with read-only databases so the value of - * this flag is ignored if the database is opened read-only. - * - */ - public static final int ENABLE_WRITE_AHEAD_LOGGING = 0x20000000; @Override public Database openDatabase(String databasePath, String password, int openFlags) { return new DefaultDatabase(SQLiteDatabase.openDatabase(databasePath, null, openFlags, new DBErrorHandler())); } - @Override - public void init(Context context) { - } - private class DBErrorHandler implements DatabaseErrorHandler { @Override public void onCorruption(SQLiteDatabase dbObj) { diff --git a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultCursor.java b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultCursor.java index 02caa59c..f4f19baa 100644 --- a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultCursor.java +++ b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultCursor.java @@ -1,14 +1,12 @@ package org.pgsqlite.sqlite.plugin; -import android.database.Cursor; - import java.io.IOException; /** * Written by Sergei Dryganets Jul 24/2017 */ -public class DefaultCursor implements ICursor { - private final Cursor mCursor; +public class DefaultCursor implements Cursor { + private final android.database.Cursor mCursor; public DefaultCursor(android.database.Cursor cursor) { mCursor = cursor; diff --git a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultDatabase.java b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultDatabase.java index 7969d69f..197aa5e3 100644 --- a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultDatabase.java +++ b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/DefaultDatabase.java @@ -25,7 +25,7 @@ public SQLStatement compileStatement(String sql) { } @Override - public ICursor rawQuery(String sql, String[] params) { + public Cursor rawQuery(String sql, String[] params) { return new DefaultCursor(database.rawQuery(sql, params)); } diff --git a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/ICursor.java b/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/ICursor.java deleted file mode 100644 index 866b95d1..00000000 --- a/src/android/sqlite-plugin-provider/src/main/java/org/pgsqlite/sqlite/plugin/ICursor.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.pgsqlite.sqlite.plugin; - -import java.io.Closeable; - -/** - * Written by Sergei Dryganets Jul 24/2017 - */ -public interface ICursor extends Closeable { - boolean moveToFirst(); - - boolean moveToNext(); - - int getColumnCount(); - - String getColumnName(int i); - - int getType(int i); - - long getLong(int i); - - double getDouble(int i); - - String getString(int i); - - byte[] getBlob(int i); - - int getCount(); -} diff --git a/src/android/storage/src/main/java/org/pgsqlite/SQLitePlugin.java b/src/android/storage/src/main/java/org/pgsqlite/SQLitePlugin.java index a0306a7a..e364f74e 100755 --- a/src/android/storage/src/main/java/org/pgsqlite/SQLitePlugin.java +++ b/src/android/storage/src/main/java/org/pgsqlite/SQLitePlugin.java @@ -9,9 +9,6 @@ import android.annotation.SuppressLint; import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteException; import android.util.Base64; import java.io.Closeable; @@ -40,7 +37,7 @@ import org.json.JSONObject; import org.pgsqlite.sqlite.plugin.Database; import org.pgsqlite.sqlite.plugin.DatabaseConnectionProvider; -import org.pgsqlite.sqlite.plugin.ICursor; +import org.pgsqlite.sqlite.plugin.Cursor; import org.pgsqlite.sqlite.plugin.SQLStatement; import java.io.FileOutputStream; @@ -234,8 +231,13 @@ private boolean executeAndPossiblyThrow(Action action, JSONArray args, CallbackC case open: o = args.getJSONObject(0); dbname = o.getString("name"); + String password = null; + + if (o.has("key")) { + password = o.getString("key"); + } // open database and start reading its queue - this.startDatabase(dbname, o, cbc); + this.startDatabase(dbname, password, o, cbc); break; case close: @@ -337,10 +339,11 @@ public void closeAllOpenDatabases() { /** * * @param dbname - The name of the database file + * @param password - password used to encrypt database * @param options - options passed in from JS * @param cbc - JS callback context */ - private void startDatabase(String dbname, JSONObject options, CallbackContext cbc) { + private void startDatabase(String dbname, String password, JSONObject options, CallbackContext cbc) { // 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(dbname); @@ -352,7 +355,7 @@ private void startDatabase(String dbname, JSONObject options, CallbackContext cb // than orphaning the old DBRunner. cbc.success("database started"); } else { - r = new DBRunner(dbname, options, cbc); + r = new DBRunner(dbname, password, options, cbc); dbrmap.put(dbname, r); this.getThreadPool().execute(r); } @@ -368,7 +371,7 @@ private void startDatabase(String dbname, JSONObject options, CallbackContext cb * @return instance of SQLite database * @throws Exception */ - private Database openDatabase(String dbname, String assetFilePath, int openFlags, CallbackContext cbc) throws Exception { + private Database openDatabase(String dbname, String password, String assetFilePath, int openFlags, CallbackContext cbc) throws Exception { InputStream in = null; File dbfile = null; try { @@ -395,7 +398,7 @@ private Database openDatabase(String dbname, String assetFilePath, int openFlags File assetFile = new File(filesDir, assetFilePath); in = new FileInputStream(assetFile); FLog.v(TAG, "Located pre-populated DB asset in Files subdirectory: " + assetFile.getCanonicalPath()); - if (openFlags == SQLiteDatabase.OPEN_READONLY) { + if (openFlags == DatabaseConnectionProvider.OPEN_READONLY) { dbfile = assetFile; FLog.v(TAG, "Detected read-only mode request for external asset."); } @@ -403,7 +406,8 @@ private Database openDatabase(String dbname, String assetFilePath, int openFlags } if (dbfile == null) { - openFlags = SQLiteDatabase.OPEN_READWRITE | SQLiteDatabase.CREATE_IF_NECESSARY; + openFlags = DatabaseConnectionProvider.OPEN_READWRITE | + DatabaseConnectionProvider.CREATE_IF_NECESSARY; dbfile = this.getContext().getDatabasePath(dbname); if (!dbfile.exists() && in != null) { @@ -418,14 +422,13 @@ private Database openDatabase(String dbname, String assetFilePath, int openFlags FLog.v(TAG, "Opening sqlite db: " + dbfile.getAbsolutePath()); - // TODO implement password support - Database mydb = provider.openDatabase(dbfile.getAbsolutePath(), null, openFlags); + Database mydb = provider.openDatabase(dbfile.getAbsolutePath(), password, openFlags); if (cbc != null) // needed for Android locking/closing workaround cbc.success("database open"); return mydb; - } catch (SQLiteException ex) { + } catch (Exception ex) { if (cbc != null) // needed for Android locking/closing workaround cbc.error("can't open database " + ex); throw ex; @@ -634,7 +637,7 @@ private void executeSqlBatch(String dbname, String[] queryarr, JSONArray[] jsonp rowsAffected = myStatement.executeUpdateDelete(); // Indicate valid results: needRawQuery = false; - } catch (SQLiteException ex) { + } catch (Exception ex) { // Indicate problem & stop this query: errorMessage = ex.getMessage(); FLog.e(TAG, "SQLiteStatement.executeUpdateDelete() failed", ex); @@ -670,7 +673,7 @@ private void executeSqlBatch(String dbname, String[] queryarr, JSONArray[] jsonp } else { queryResult.put("rowsAffected", 0); } - } catch (SQLiteException ex) { + } catch (Exception ex) { // report error result with the error message // could be constraint violation or some other error errorMessage = ex.getMessage(); @@ -687,7 +690,7 @@ private void executeSqlBatch(String dbname, String[] queryarr, JSONArray[] jsonp queryResult = new JSONObject(); queryResult.put("rowsAffected", 0); - } catch (SQLiteException ex) { + } catch (Exception ex) { errorMessage = ex.getMessage(); FLog.e(TAG, "SQLiteDatabase.beginTransaction() failed", ex); } @@ -701,7 +704,7 @@ private void executeSqlBatch(String dbname, String[] queryarr, JSONArray[] jsonp queryResult = new JSONObject(); queryResult.put("rowsAffected", 0); - } catch (SQLiteException ex) { + } catch (Exception ex) { errorMessage = ex.getMessage(); FLog.e(TAG, "SQLiteDatabase.setTransactionSuccessful/endTransaction() failed", ex); } @@ -714,7 +717,7 @@ private void executeSqlBatch(String dbname, String[] queryarr, JSONArray[] jsonp queryResult = new JSONObject(); queryResult.put("rowsAffected", 0); - } catch (SQLiteException ex) { + } catch (Exception ex) { errorMessage = ex.getMessage(); FLog.e(TAG, "SQLiteDatabase.endTransaction() failed", ex); } @@ -798,7 +801,7 @@ private JSONObject executeSqlStatementQuery(Database mydb, CallbackContext cbc) throws Exception { JSONObject rowsResult = new JSONObject(); - ICursor cur = null; + Cursor cur = null; try { try { String[] params; @@ -855,7 +858,7 @@ private JSONObject executeSqlStatementQuery(Database mydb, } @SuppressLint("NewApi") - private void bindRow(JSONObject row, String key, ICursor cur, int i) throws JSONException { + private void bindRow(JSONObject row, String key, Cursor cur, int i) throws JSONException { int curType = cur.getType(i); switch (curType) { @@ -890,6 +893,7 @@ private void closeQuietly(Closeable closeable) { private class DBRunner implements Runnable { final String dbname; + final String password; final int openFlags; private String assetFilename; private boolean androidLockWorkaround; @@ -898,14 +902,16 @@ private class DBRunner implements Runnable { Database mydb; - DBRunner(final String dbname, JSONObject options, CallbackContext cbc) { + DBRunner(final String dbname, final String password, JSONObject options, CallbackContext cbc) { this.dbname = dbname; - int openFlags = SQLiteDatabase.OPEN_READWRITE | SQLiteDatabase.CREATE_IF_NECESSARY; + this.password = password; + int openFlags = DatabaseConnectionProvider.OPEN_READWRITE | + DatabaseConnectionProvider.CREATE_IF_NECESSARY; try { this.assetFilename = options.has("assetFilename") ? options.getString("assetFilename") : null; if (this.assetFilename != null && this.assetFilename.length() > 0) { boolean readOnly = options.has("readOnly") && options.getBoolean("readOnly"); - openFlags = readOnly ? SQLiteDatabase.OPEN_READONLY : openFlags; + openFlags = readOnly ? DatabaseConnectionProvider.OPEN_READONLY : openFlags; } } catch (Exception ex){ FLog.e(TAG,"Error retrieving assetFilename this.mode from options:",ex); @@ -921,7 +927,7 @@ private class DBRunner implements Runnable { public void run() { try { - this.mydb = openDatabase(dbname, this.assetFilename, this.openFlags, this.openCbc); + this.mydb = openDatabase(dbname, password, this.assetFilename, this.openFlags, this.openCbc); } catch (Exception e) { FLog.e(TAG, "unexpected error, stopping db thread", e); dbrmap.remove(dbname); @@ -940,7 +946,7 @@ public void run() { if (androidLockWorkaround && dbq.queries.length == 1 && dbq.queries[0].equals("COMMIT")) { // FLog.v(TAG, "close and reopen db"); closeDatabaseNow(dbname); - this.mydb = openDatabase(dbname, "", this.openFlags, null); + this.mydb = openDatabase(dbname, password, "", this.openFlags, null); // FLog.v(TAG, "close and reopen db finished"); } From da6b6226a2bad66546b6ca66fbc0d7badd422512 Mon Sep 17 00:00:00 2001 From: Sergei Dryganets Date: Sun, 30 Jul 2017 20:59:54 -0700 Subject: [PATCH 3/4] Documentation update --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3f2c3ac0..553657e5 100644 --- a/README.md +++ b/README.md @@ -252,7 +252,7 @@ dependencies { ... compile project(':react-native-sqlite-storage') compile project(':sqlite-plugin-provider') - // sqlite cipher support: + // To add sqlite cipher support: compile project(':sql-cipher-plugin') } ``` @@ -277,7 +277,7 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand .setBundleAssetName("index.android.bundle") // this is dependant on how you name you JS files, example assumes index.android.js .setJSMainModuleName("index.android") // this is dependant on how you name you JS files, example assumes index.android.js .addPackage(new MainReactPackage()) - .addPackage(new SQLitePluginPackage()) // register SQLite Plugin here + .addPackage(new SQLitePluginPackage()) // register SQLite Plugin here. For SQLiteCipher support pass SqliteCipherConnectionProvider to PluginPackage. .setUseDeveloperSupport(BuildConfig.DEBUG) .setInitialLifecycleState(LifecycleState.RESUMED) .build(); From 2b73654f6835c12d630e7a2f9f57c395020d9f20 Mon Sep 17 00:00:00 2001 From: Sergei Dryganets Date: Sun, 30 Jul 2017 21:09:01 -0700 Subject: [PATCH 4/4] Package has the same code style with rest of the code base now. --- .../src/main/java/org/pgsqlite/SQLitePluginPackage.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/android/storage/src/main/java/org/pgsqlite/SQLitePluginPackage.java b/src/android/storage/src/main/java/org/pgsqlite/SQLitePluginPackage.java index 90e3a6ef..9dde94c5 100644 --- a/src/android/storage/src/main/java/org/pgsqlite/SQLitePluginPackage.java +++ b/src/android/storage/src/main/java/org/pgsqlite/SQLitePluginPackage.java @@ -5,8 +5,6 @@ */ package org.pgsqlite; -import android.app.Activity; - import com.facebook.react.ReactPackage; import com.facebook.react.bridge.JavaScriptModule; import com.facebook.react.bridge.NativeModule; @@ -23,14 +21,14 @@ public class SQLitePluginPackage implements ReactPackage { - private final DatabaseConnectionProvider mProvider; + private final DatabaseConnectionProvider provider; public SQLitePluginPackage() { this(new DefaultConnectionProvider()); } public SQLitePluginPackage(DatabaseConnectionProvider provider) { - mProvider = provider; + this.provider = provider; } @Override @@ -38,7 +36,7 @@ public List createNativeModules( ReactApplicationContext reactContext) { List modules = new ArrayList<>(); - modules.add(new SQLitePlugin(reactContext, mProvider)); + modules.add(new SQLitePlugin(reactContext, provider)); return modules; }