diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b0fce183..436e69ae 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -53,17 +53,62 @@ jobs:
uses: actions/cache@v3
with:
path: ${{ env.TURBO_CACHE_DIR }}
- key: ${{ runner.os }}-turborepo-android-${{ hashFiles('yarn.lock') }}
+ key: ${{ runner.os }}-turborepo-android-${{ hashFiles('yarn.lock', 'cpp/**') }}
restore-keys: |
${{ runner.os }}-turborepo-android-
- - name: Check turborepo cache for Android
+ # - name: Check turborepo cache for Android
+ # run: |
+ # TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:android').cache.status")
+
+ # if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then
+ # echo "turbo_cache_hit=1" >> $GITHUB_ENV
+ # fi
+
+ - name: Cache Gradle
+ if: env.turbo_cache_hit != 1
+ uses: actions/cache@v3
+ with:
+ path: |
+ ~/.gradle/wrapper
+ ~/.gradle/caches
+ key: ${{ runner.os }}-gradle-${{ hashFiles('example/android/gradle/wrapper/gradle-wrapper.properties') }}
+ restore-keys: |
+ ${{ runner.os }}-gradle-
+
+ - name: Build example for Android
+ working-directory: example
+ env:
+ JAVA_OPTS: '-XX:MaxHeapSize=6g'
run: |
- TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:android').cache.status")
+ yarn run build:android
- if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then
- echo "turbo_cache_hit=1" >> $GITHUB_ENV
- fi
+ build-android-sqlcipher:
+ runs-on: self-hosted
+ env:
+ TURBO_CACHE_DIR: .turbo/android
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup
+ uses: ./.github/actions/setup
+
+ - name: Cache turborepo for Android
+ uses: actions/cache@v3
+ with:
+ path: ${{ env.TURBO_CACHE_DIR }}
+ key: ${{ runner.os }}-turborepo-android-${{ hashFiles('yarn.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-turborepo-android-
+
+ # - name: Check turborepo cache for Android
+ # run: |
+ # TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:android').cache.status")
+
+ # if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then
+ # echo "turbo_cache_hit=1" >> $GITHUB_ENV
+ # fi
- name: Cache Gradle
if: env.turbo_cache_hit != 1
@@ -77,10 +122,11 @@ jobs:
${{ runner.os }}-gradle-
- name: Build example for Android
+ working-directory: example
env:
JAVA_OPTS: '-XX:MaxHeapSize=6g'
run: |
- yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}"
+ OP_SQLITE_USE_SQLCIPHER=1 yarn run build:android
build-ios:
runs-on: self-hosted
@@ -106,13 +152,13 @@ jobs:
restore-keys: |
${{ runner.os }}-turborepo-ios-
- - name: Check turborepo cache for iOS
- run: |
- TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:ios').cache.status")
+ # - name: Check turborepo cache for iOS
+ # run: |
+ # TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:ios').cache.status")
- if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then
- echo "turbo_cache_hit=1" >> $GITHUB_ENV
- fi
+ # if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then
+ # echo "turbo_cache_hit=1" >> $GITHUB_ENV
+ # fi
- name: Cache cocoapods
if: env.turbo_cache_hit != 1
@@ -134,8 +180,9 @@ jobs:
NO_FLIPPER: 1
- name: Build example for iOS
+ working-directory: example
run: |
- yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}"
+ yarn run build:ios
build-ios-sqlcipher:
runs-on: self-hosted
@@ -161,13 +208,13 @@ jobs:
restore-keys: |
${{ runner.os }}-turborepo-ios-
- - name: Check turborepo cache for iOS
- run: |
- TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:ios').cache.status")
+ # - name: Check turborepo cache for iOS
+ # run: |
+ # TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:ios').cache.status")
- if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then
- echo "turbo_cache_hit=1" >> $GITHUB_ENV
- fi
+ # if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then
+ # echo "turbo_cache_hit=1" >> $GITHUB_ENV
+ # fi
- name: Cache cocoapods
if: env.turbo_cache_hit != 1
@@ -189,49 +236,6 @@ jobs:
NO_FLIPPER: 1
- name: Build example for iOS
+ working-directory: example
run: |
- yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}"
-
- build-android-sqlcipher:
- runs-on: self-hosted
- env:
- TURBO_CACHE_DIR: .turbo/android
- steps:
- - name: Checkout
- uses: actions/checkout@v4
-
- - name: Setup
- uses: ./.github/actions/setup
-
- - name: Cache turborepo for Android
- uses: actions/cache@v3
- with:
- path: ${{ env.TURBO_CACHE_DIR }}
- key: ${{ runner.os }}-turborepo-android-${{ hashFiles('yarn.lock') }}
- restore-keys: |
- ${{ runner.os }}-turborepo-android-
-
- - name: Check turborepo cache for Android
- run: |
- TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:android').cache.status")
-
- if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then
- echo "turbo_cache_hit=1" >> $GITHUB_ENV
- fi
-
- - name: Cache Gradle
- if: env.turbo_cache_hit != 1
- uses: actions/cache@v3
- with:
- path: |
- ~/.gradle/wrapper
- ~/.gradle/caches
- key: ${{ runner.os }}-gradle-${{ hashFiles('example/android/gradle/wrapper/gradle-wrapper.properties') }}
- restore-keys: |
- ${{ runner.os }}-gradle-
-
- - name: Build example for Android
- env:
- JAVA_OPTS: '-XX:MaxHeapSize=6g'
- run: |
- OP_SQLITE_USE_SQLCIPHER=1 yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}"
+ yarn run build:ios
diff --git a/android/.project b/android/.project
index 0e0a1bac..c243e249 100644
--- a/android/.project
+++ b/android/.project
@@ -5,6 +5,11 @@
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
org.eclipse.buildship.core.gradleprojectbuilder
@@ -12,6 +17,18 @@
+ org.eclipse.jdt.core.javanature
org.eclipse.buildship.core.gradleprojectnature
+
+
+ 1712182270120
+
+ 30
+
+ org.eclipse.core.resources.regexFilterMatcher
+ node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__
+
+
+
diff --git a/android/.settings/org.eclipse.buildship.core.prefs b/android/.settings/org.eclipse.buildship.core.prefs
index 8c253d67..c17f03e1 100644
--- a/android/.settings/org.eclipse.buildship.core.prefs
+++ b/android/.settings/org.eclipse.buildship.core.prefs
@@ -2,7 +2,7 @@ arguments=
auto.sync=false
build.scans.enabled=false
connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(6.0))
-connection.project.dir=
+connection.project.dir=../example/android
eclipse.preferences.version=1
gradle.user.home=
java.home=/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home
diff --git a/android/CMakeLists.txt b/android/CMakeLists.txt
index 2d38f0ce..abfbaea2 100644
--- a/android/CMakeLists.txt
+++ b/android/CMakeLists.txt
@@ -20,6 +20,16 @@ add_library(
SHARED
../cpp/bridge.cpp
../cpp/bridge.h
+ ../cpp/DbHostObject.cpp
+ ../cpp/DbHostObject.h
+ ../cpp/validators/DbOpenValidator.cpp
+ ../cpp/validators/DbOpenValidator.h
+ ../cpp/validators/DbAttachValidator.cpp
+ ../cpp/validators/DbAttachValidator.h
+ ../cpp/validators/DbDetachValidator.cpp
+ ../cpp/validators/DbDetachValidator.h
+ ../cpp/validators/DbCloseValidator.cpp
+ ../cpp/validators/DbCloseValidator.h
../cpp/bindings.cpp
../cpp/bindings.h
../cpp/utils.h
diff --git a/cpp/DbHostObject.cpp b/cpp/DbHostObject.cpp
new file mode 100644
index 00000000..ebe733bf
--- /dev/null
+++ b/cpp/DbHostObject.cpp
@@ -0,0 +1,177 @@
+//
+// Created by jplc on 4/2/24.
+//
+
+#include "DbHostObject.h"
+#include "bridge.h"
+#include "macros.h"
+#include "types.h"
+#include "validators/DbAttachValidator.h"
+#include "validators/DbCloseValidator.h"
+#include "validators/DbDetachValidator.h"
+#include "validators/DbOpenValidator.h"
+#include
+
+namespace opsqlite {
+
+const std::string DbHostObject::F_OPEN = "open";
+const int DbHostObject::F_OPEN_ARGS_COUNT = 3;
+const std::string DbHostObject::F_ATTACH = "attach";
+const int DbHostObject::F_ATTACH_ARGS_COUNT = 4;
+const std::string DbHostObject::F_DETACH = "detach";
+const int DbHostObject::F_DETACH_ARGS_COUNT = 2;
+const std::string DbHostObject::F_CLOSE = "close";
+const int DbHostObject::F_CLOSE_ARGS_COUNT = 1;
+
+jsi::Value DbHostObject::get(jsi::Runtime &runtime,
+ const jsi::PropNameID &propNameId) {
+ auto methodName = propNameId.utf8(runtime);
+ if (methodName == F_OPEN) {
+ return &DbHostObject::open;
+ } else if (methodName == F_ATTACH) {
+ return &DbHostObject::attach;
+ } else if (methodName == F_DETACH) {
+ return &DbHostObject::detach;
+ } else if (methodName == F_CLOSE) {
+ return &DbHostObject::close;
+ }
+ return nullptr;
+}
+
+void DbHostObject::set(jsi::Runtime &runtime, const jsi::PropNameID &propNameId,
+ const jsi::Value &value) {
+ // no attributes to set at moment.
+}
+
+std::vector
+DbHostObject::getPropertyNames(jsi::Runtime &runtime) {
+ std::vector properties;
+ properties.push_back(jsi::PropNameID::forAscii(runtime, F_OPEN));
+ properties.push_back(jsi::PropNameID::forAscii(runtime, F_ATTACH));
+ properties.push_back(jsi::PropNameID::forAscii(runtime, F_DETACH));
+ properties.push_back(jsi::PropNameID::forAscii(runtime, F_CLOSE));
+ return properties;
+}
+
+jsi::Function DbHostObject::open(jsi::Runtime &rt,
+ const std::string &basePath) {
+ using opsqlite::validators::DbOpenValidator;
+ return HOSTFN(F_OPEN, F_OPEN_ARGS_COUNT) {
+ std::string errMsg;
+ if (DbOpenValidator::invalidArgsNumber(errMsg, count)) {
+ throw std::runtime_error(errMsg);
+ }
+
+ jsi::Object options = args[0].asObject(rt);
+ std::string dbName = options.getProperty(rt, "name").asString(rt).utf8(rt);
+ std::string path = DbOpenValidator::getPath(rt, options, basePath);
+ std::string encryptionKey = DbOpenValidator::getEncryptionKey(rt, options);
+
+#ifdef OP_SQLITE_USE_SQLCIPHER
+ if (encryptionKey.empty()) {
+ throw std::runtime_error(
+ "[OP SQLite] using SQLCipher encryption key is required");
+ }
+ // TODO(osp) find a way to display the yellow box from c++
+ BridgeResult result = opsqlite_open(dbName, path, encryptionKey);
+#else
+ // if (!encryptionKey.empty()) {
+ // // RCTLogWarn(@"Your message")
+ // throw std::runtime_error("[OP SQLite] SQLCipher is not enabled, "
+ // "encryption key is not allowed");
+ // }
+ BridgeResult result = opsqlite_open(dbName, path);
+#endif
+
+ if (result.type == SQLiteError) {
+ throw std::runtime_error(result.message);
+ }
+
+ return {};
+ });
+}
+
+jsi::Function DbHostObject::attach(jsi::Runtime &rt,
+ const std::string &basePath) {
+ using opsqlite::validators::DbAttachValidator;
+ return HOSTFN(F_ATTACH, F_ATTACH_ARGS_COUNT) {
+ std::string errMsg;
+ if (DbAttachValidator::invalidArgsNumber(errMsg, count)) {
+ throw jsi::JSError(rt, errMsg);
+ }
+ if (DbAttachValidator::noStringArgs(errMsg, args[0], args[1], args[2])) {
+ throw jsi::JSError(rt, errMsg);
+ return {};
+ }
+
+ std::string tempDocPath = std::string(basePath);
+ if (DbAttachValidator::locationArgDefined(count, args[3])) {
+ if (DbAttachValidator::locationArgIsNotString(errMsg, args[3])) {
+ throw std::runtime_error(errMsg);
+ }
+ tempDocPath = tempDocPath + "/" + args[3].asString(rt).utf8(rt);
+ }
+
+ std::string dbName = args[0].asString(rt).utf8(rt);
+ std::string databaseToAttach = args[1].asString(rt).utf8(rt);
+ std::string alias = args[2].asString(rt).utf8(rt);
+ BridgeResult result =
+ opsqlite_attach(dbName, tempDocPath, databaseToAttach, alias);
+
+ if (result.type == SQLiteError) {
+ throw std::runtime_error(result.message);
+ }
+
+ return {};
+ });
+}
+
+jsi::Function DbHostObject::detach(jsi::Runtime &rt) {
+ using opsqlite::validators::DbDetachValidator;
+ return HOSTFN(F_DETACH, F_DETACH_ARGS_COUNT) {
+ std::string errMsg;
+ if (DbDetachValidator::invalidArgsNumber(errMsg, count)) {
+ throw std::runtime_error(errMsg);
+ }
+ if (DbDetachValidator::noStringArgs(errMsg, args[0], args[1])) {
+ throw std::runtime_error(errMsg);
+ return {};
+ }
+
+ std::string dbName = args[0].asString(rt).utf8(rt);
+ std::string alias = args[1].asString(rt).utf8(rt);
+ BridgeResult result = opsqlite_detach(dbName, alias);
+
+ if (result.type == SQLiteError) {
+ throw jsi::JSError(rt, result.message.c_str());
+ }
+
+ return {};
+ });
+}
+
+jsi::Function DbHostObject::close(jsi::Runtime &rt) {
+ using opsqlite::validators::DbCloseValidator;
+ return HOSTFN(F_CLOSE, F_CLOSE_ARGS_COUNT) {
+ std::string errMsg;
+ if (DbCloseValidator::invalidArgsNumber(errMsg, count)) {
+ throw std::runtime_error(errMsg);
+ }
+
+ if (DbCloseValidator::noStringArgs(errMsg, args[0])) {
+ throw std::runtime_error(errMsg);
+ }
+
+ std::string dbName = args[0].asString(rt).utf8(rt);
+
+ BridgeResult result = opsqlite_close(dbName);
+
+ if (result.type == SQLiteError) {
+ throw jsi::JSError(rt, result.message.c_str());
+ }
+
+ return {};
+ });
+}
+
+} // namespace opsqlite
diff --git a/cpp/DbHostObject.h b/cpp/DbHostObject.h
new file mode 100644
index 00000000..bde03085
--- /dev/null
+++ b/cpp/DbHostObject.h
@@ -0,0 +1,41 @@
+//
+// Created by jplc on 4/2/24.
+//
+
+#ifndef OPSQLITEEXAMPLE_DBHOSTOBJECT_H
+#define OPSQLITEEXAMPLE_DBHOSTOBJECT_H
+
+#include
+#include
+
+namespace jsi = facebook::jsi;
+
+namespace opsqlite {
+
+class JSI_EXPORT DbHostObject : public jsi::HostObject {
+public:
+ jsi::Value get(jsi::Runtime &runtime,
+ const jsi::PropNameID &propNameId) override;
+ void set(jsi::Runtime &runtime, const jsi::PropNameID &propNameId,
+ const jsi::Value &value) override;
+ std::vector getPropertyNames(jsi::Runtime &runtime) override;
+
+ static jsi::Function open(jsi::Runtime &rt, const std::string &basePath);
+ static jsi::Function attach(jsi::Runtime &rt, const std::string &basePath);
+ static jsi::Function detach(jsi::Runtime &rt);
+ static jsi::Function close(jsi::Runtime &rt);
+
+private:
+ static const std::string F_OPEN; // open
+ static const int F_OPEN_ARGS_COUNT;
+ static const std::string F_ATTACH; // attach
+ static const int F_ATTACH_ARGS_COUNT;
+ static const std::string F_DETACH; // detach
+ static const int F_DETACH_ARGS_COUNT;
+ static const std::string F_CLOSE; // close
+ static const int F_CLOSE_ARGS_COUNT;
+};
+
+} // namespace opsqlite
+
+#endif // OPSQLITEEXAMPLE_DBHOSTOBJECT_H
diff --git a/cpp/bindings.cpp b/cpp/bindings.cpp
index a884f32c..5d91b348 100644
--- a/cpp/bindings.cpp
+++ b/cpp/bindings.cpp
@@ -8,10 +8,13 @@
#include "sqlbatchexecutor.h"
#include "utils.h"
#include
+#include
#include
#include
#include
+#include "DbHostObject.h"
+
namespace opsqlite {
namespace jsi = facebook::jsi;
@@ -49,139 +52,13 @@ void install(jsi::Runtime &rt,
basePath = std::string(docPath);
invoker = jsCallInvoker;
- auto open = HOSTFN("open", 3) {
- if (count == 0) {
- throw std::runtime_error("[op-sqlite][open] database name is required");
- }
-
- jsi::Object options = args[0].asObject(rt);
- std::string dbName = options.getProperty(rt, "name").asString(rt).utf8(rt);
- std::string path = std::string(basePath);
- std::string location;
- std::string encryptionKey;
-
- if (options.hasProperty(rt, "location")) {
- location = options.getProperty(rt, "location").asString(rt).utf8(rt);
- }
-
- if (options.hasProperty(rt, "encryptionKey")) {
- encryptionKey =
- options.getProperty(rt, "encryptionKey").asString(rt).utf8(rt);
- }
-
-#ifdef OP_SQLITE_USE_SQLCIPHER
- if (encryptionKey.empty()) {
- throw std::runtime_error(
- "[OP SQLite] using SQLCipher encryption key is required");
- }
-// TODO(osp) find a way to display the yellow box from c++
-#else
- // if (!encryptionKey.empty()) {
- // // RCTLogWarn(@"Your message")
- // throw std::runtime_error("[OP SQLite] SQLCipher is not enabled, "
- // "encryption key is not allowed");
- // }
-#endif
-
- if (!location.empty()) {
- if (location == ":memory:") {
- path = ":memory:";
- } else if (location.rfind("/", 0) == 0) {
- path = location;
- } else {
- path = path + "/" + location;
- }
- }
-
-#ifdef OP_SQLITE_USE_SQLCIPHER
- BridgeResult result = opsqlite_open(dbName, path, encryptionKey);
-#else
- BridgeResult result = opsqlite_open(dbName, path);
-#endif
-
- if (result.type == SQLiteError) {
- throw std::runtime_error(result.message);
- }
-
- return {};
- });
-
- auto attach = HOSTFN("attach", 4) {
- if (count < 3) {
- throw jsi::JSError(rt,
- "[op-sqlite][attach] Incorrect number of arguments");
- }
- if (!args[0].isString() || !args[1].isString() || !args[2].isString()) {
- throw jsi::JSError(
- rt, "dbName, databaseToAttach and alias must be a strings");
- return {};
- }
-
- std::string tempDocPath = std::string(basePath);
- if (count > 3 && !args[3].isUndefined() && !args[3].isNull()) {
- if (!args[3].isString()) {
- throw std::runtime_error(
- "[op-sqlite][attach] database location must be a string");
- }
-
- tempDocPath = tempDocPath + "/" + args[3].asString(rt).utf8(rt);
- }
-
- std::string dbName = args[0].asString(rt).utf8(rt);
- std::string databaseToAttach = args[1].asString(rt).utf8(rt);
- std::string alias = args[2].asString(rt).utf8(rt);
- BridgeResult result =
- opsqlite_attach(dbName, tempDocPath, databaseToAttach, alias);
-
- if (result.type == SQLiteError) {
- throw std::runtime_error(result.message);
- }
-
- return {};
- });
-
- auto detach = HOSTFN("detach", 2) {
- if (count < 2) {
- throw std::runtime_error(
- "[op-sqlite][detach] Incorrect number of arguments");
- }
- if (!args[0].isString() || !args[1].isString()) {
- throw std::runtime_error(
- "dbName, databaseToAttach and alias must be a strings");
- return {};
- }
-
- std::string dbName = args[0].asString(rt).utf8(rt);
- std::string alias = args[1].asString(rt).utf8(rt);
- BridgeResult result = opsqlite_detach(dbName, alias);
-
- if (result.type == SQLiteError) {
- throw jsi::JSError(rt, result.message.c_str());
- }
-
- return {};
- });
-
- auto close = HOSTFN("close", 1) {
- if (count == 0) {
- throw std::runtime_error("[op-sqlite][close] database name is required");
- }
-
- if (!args[0].isString()) {
- throw std::runtime_error(
- "[op-sqlite][close] database name must be a string");
- }
+ auto open = DbHostObject::open(rt, basePath);
- std::string dbName = args[0].asString(rt).utf8(rt);
+ auto attach = DbHostObject::attach(rt, basePath);
- BridgeResult result = opsqlite_close(dbName);
+ auto detach = DbHostObject::detach(rt);
- if (result.type == SQLiteError) {
- throw jsi::JSError(rt, result.message.c_str());
- }
-
- return {};
- });
+ auto close = DbHostObject::close(rt);
auto remove = HOSTFN("delete", 2) {
if (count == 0) {
@@ -301,18 +178,18 @@ void install(jsi::Runtime &rt,
});
auto execute_async = HOSTFN("executeAsync", 3) {
- if (count < 3) {
- throw std::runtime_error(
- "[op-sqlite][executeAsync] Incorrect arguments for executeAsync");
- }
+ if (count < 3) {
+ throw std::runtime_error(
+ "[op-sqlite][executeAsync] Incorrect arguments for executeAsync");
+ }
- const std::string dbName = args[0].asString(rt).utf8(rt);
- const std::string query = args[1].asString(rt).utf8(rt);
- const jsi::Value &originalParams = args[2];
+ const std::string dbName = args[0].asString(rt).utf8(rt);
+ const std::string query = args[1].asString(rt).utf8(rt);
+ const jsi::Value &originalParams = args[2];
- std::vector params = toVariantVec(rt, originalParams);
+ std::vector params = toVariantVec(rt, originalParams);
- auto promiseCtr = rt.global().getPropertyAsFunction(rt, "Promise");
+ auto promiseCtr = rt.global().getPropertyAsFunction(rt, "Promise");
auto promise = promiseCtr.callAsConstructor(rt, HOSTFN("executor", 2) {
auto resolve = std::make_shared(rt, args[0]);
@@ -455,16 +332,16 @@ void install(jsi::Runtime &rt,
});
auto load_file = HOSTFN("loadFile", 2) {
- if (sizeof(args) < 2) {
- throw std::runtime_error(
- "[op-sqlite][loadFile] Incorrect parameter count");
- return {};
- }
+ if (sizeof(args) < 2) {
+ throw std::runtime_error(
+ "[op-sqlite][loadFile] Incorrect parameter count");
+ return {};
+ }
- const std::string dbName = args[0].asString(rt).utf8(rt);
- const std::string sqlFileName = args[1].asString(rt).utf8(rt);
+ const std::string dbName = args[0].asString(rt).utf8(rt);
+ const std::string sqlFileName = args[1].asString(rt).utf8(rt);
- auto promiseCtr = rt.global().getPropertyAsFunction(rt, "Promise");
+ auto promiseCtr = rt.global().getPropertyAsFunction(rt, "Promise");
auto promise = promiseCtr.callAsConstructor(rt, HOSTFN("executor", 2) {
auto resolve = std::make_shared(rt, args[0]);
auto reject = std::make_shared(rt, args[1]);
@@ -501,162 +378,167 @@ void install(jsi::Runtime &rt,
});
auto update_hook = HOSTFN("updateHook", 2) {
- if (sizeof(args) < 2) {
- throw std::runtime_error("[op-sqlite][updateHook] Incorrect parameters: "
- "dbName and callback needed");
- return {};
- }
+ if (sizeof(args) < 2) {
+ throw std::runtime_error(
+ "[op-sqlite][updateHook] Incorrect parameters: "
+ "dbName and callback needed");
+ return {};
+ }
- auto dbName = args[0].asString(rt).utf8(rt);
- auto callback = std::make_shared(rt, args[1]);
+ auto dbName = args[0].asString(rt).utf8(rt);
+ auto callback = std::make_shared(rt, args[1]);
- if (callback->isUndefined() || callback->isNull()) {
- opsqlite_deregister_update_hook(dbName);
- return {};
- }
+ if (callback->isUndefined() || callback->isNull()) {
+ opsqlite_deregister_update_hook(dbName);
+ return {};
+ }
- updateHooks[dbName] = callback;
+ updateHooks[dbName] = callback;
- auto hook = [&rt, callback](std::string dbName, std::string tableName,
- std::string operation, int rowId) {
- std::vector params;
- std::vector results;
- std::shared_ptr> metadata =
- std::make_shared>();
+ auto hook = [&rt, callback](std::string dbName, std::string tableName,
+ std::string operation, int rowId) {
+ std::vector params;
+ std::vector results;
+ std::shared_ptr> metadata =
+ std::make_shared>();
- if (operation != "DELETE") {
- std::string query = "SELECT * FROM " + tableName +
- " where rowid = " + std::to_string(rowId) + ";";
- opsqlite_execute(dbName, query, ¶ms, &results, metadata);
- }
+ if (operation != "DELETE") {
+ std::string query = "SELECT * FROM " + tableName +
+ " where rowid = " + std::to_string(rowId) + ";";
+ opsqlite_execute(dbName, query, ¶ms, &results, metadata);
+ }
- invoker->invokeAsync(
- [&rt,
- results = std::make_shared>(results),
- callback, tableName = std::move(tableName),
- operation = std::move(operation), &rowId] {
- auto res = jsi::Object(rt);
- res.setProperty(rt, "table",
- jsi::String::createFromUtf8(rt, tableName));
- res.setProperty(rt, "operation",
- jsi::String::createFromUtf8(rt, operation));
- res.setProperty(rt, "rowId", jsi::Value(rowId));
- if (results->size() != 0) {
- res.setProperty(
- rt, "row",
- jsi::Object::createFromHostObject(
- rt, std::make_shared(results->at(0))));
- }
+ invoker->invokeAsync(
+ [&rt,
+ results = std::make_shared>(results),
+ callback, tableName = std::move(tableName),
+ operation = std::move(operation), &rowId] {
+ auto res = jsi::Object(rt);
+ res.setProperty(rt, "table",
+ jsi::String::createFromUtf8(rt, tableName));
+ res.setProperty(rt, "operation",
+ jsi::String::createFromUtf8(rt, operation));
+ res.setProperty(rt, "rowId", jsi::Value(rowId));
+ if (results->size() != 0) {
+ res.setProperty(rt, "row",
+ jsi::Object::createFromHostObject(
+ rt, std::make_shared(
+ results->at(0))));
+ }
- callback->asObject(rt).asFunction(rt).call(rt, res);
- });
- };
+ callback->asObject(rt).asFunction(rt).call(rt, res);
+ });
+ };
- opsqlite_register_update_hook(dbName, std::move(hook));
+ opsqlite_register_update_hook(dbName, std::move(hook));
- return {};
+ return {};
});
auto commit_hook = HOSTFN("commitHook", 2) {
- if (sizeof(args) < 2) {
- throw std::runtime_error("[op-sqlite][commitHook] Incorrect parameters: "
- "dbName and callback needed");
- return {};
- }
+ if (sizeof(args) < 2) {
+ throw std::runtime_error(
+ "[op-sqlite][commitHook] Incorrect parameters: "
+ "dbName and callback needed");
+ return {};
+ }
- auto dbName = args[0].asString(rt).utf8(rt);
- auto callback = std::make_shared(rt, args[1]);
- if (callback->isUndefined() || callback->isNull()) {
- opsqlite_deregister_commit_hook(dbName);
- return {};
- }
- commitHooks[dbName] = callback;
+ auto dbName = args[0].asString(rt).utf8(rt);
+ auto callback = std::make_shared(rt, args[1]);
+ if (callback->isUndefined() || callback->isNull()) {
+ opsqlite_deregister_commit_hook(dbName);
+ return {};
+ }
+ commitHooks[dbName] = callback;
- auto hook = [&rt, callback](std::string dbName) {
- invoker->invokeAsync(
- [&rt, callback] { callback->asObject(rt).asFunction(rt).call(rt); });
- };
+ auto hook = [&rt, callback](std::string dbName) {
+ invoker->invokeAsync([&rt, callback] {
+ callback->asObject(rt).asFunction(rt).call(rt);
+ });
+ };
- opsqlite_register_commit_hook(dbName, std::move(hook));
+ opsqlite_register_commit_hook(dbName, std::move(hook));
- return {};
+ return {};
});
auto rollback_hook = HOSTFN("rollbackHook", 2) {
- if (sizeof(args) < 2) {
- throw std::runtime_error(
- "[op-sqlite][rollbackHook] Incorrect parameters: "
- "dbName and callback needed");
- return {};
- }
+ if (sizeof(args) < 2) {
+ throw std::runtime_error(
+ "[op-sqlite][rollbackHook] Incorrect parameters: "
+ "dbName and callback needed");
+ return {};
+ }
- auto dbName = args[0].asString(rt).utf8(rt);
- auto callback = std::make_shared(rt, args[1]);
+ auto dbName = args[0].asString(rt).utf8(rt);
+ auto callback = std::make_shared(rt, args[1]);
- if (callback->isUndefined() || callback->isNull()) {
- opsqlite_deregister_rollback_hook(dbName);
- return {};
- }
- rollbackHooks[dbName] = callback;
+ if (callback->isUndefined() || callback->isNull()) {
+ opsqlite_deregister_rollback_hook(dbName);
+ return {};
+ }
+ rollbackHooks[dbName] = callback;
- auto hook = [&rt, callback](std::string dbName) {
- invoker->invokeAsync(
- [&rt, callback] { callback->asObject(rt).asFunction(rt).call(rt); });
- };
+ auto hook = [&rt, callback](std::string dbName) {
+ invoker->invokeAsync([&rt, callback] {
+ callback->asObject(rt).asFunction(rt).call(rt);
+ });
+ };
- opsqlite_register_rollback_hook(dbName, std::move(hook));
- return {};
+ opsqlite_register_rollback_hook(dbName, std::move(hook));
+ return {};
});
auto prepare_statement = HOSTFN("prepareStatement", 1) {
- auto dbName = args[0].asString(rt).utf8(rt);
- auto query = args[1].asString(rt).utf8(rt);
+ auto dbName = args[0].asString(rt).utf8(rt);
+ auto query = args[1].asString(rt).utf8(rt);
- sqlite3_stmt *statement = opsqlite_prepare_statement(dbName, query);
+ sqlite3_stmt *statement = opsqlite_prepare_statement(dbName, query);
- auto preparedStatementHostObject =
- std::make_shared(dbName, statement);
+ auto preparedStatementHostObject =
+ std::make_shared(dbName, statement);
- return jsi::Object::createFromHostObject(rt, preparedStatementHostObject);
+ return jsi::Object::createFromHostObject(rt,
+ preparedStatementHostObject);
});
auto load_extension = HOSTFN("loadExtension", 2) {
- auto db_name = args[0].asString(rt).utf8(rt);
- auto path = args[1].asString(rt).utf8(rt);
- std::string entryPoint = "";
- if (count > 2 && args[2].isString()) {
- entryPoint = args[2].asString(rt).utf8(rt);
- }
+ auto db_name = args[0].asString(rt).utf8(rt);
+ auto path = args[1].asString(rt).utf8(rt);
+ std::string entryPoint = "";
+ if (count > 2 && args[2].isString()) {
+ entryPoint = args[2].asString(rt).utf8(rt);
+ }
- auto result = opsqlite_load_extension(db_name, path, entryPoint);
- if (result.type == SQLiteError) {
- throw std::runtime_error(result.message);
- }
- return {};
+ auto result = opsqlite_load_extension(db_name, path, entryPoint);
+ if (result.type == SQLiteError) {
+ throw std::runtime_error(result.message);
+ }
+ return {};
});
auto get_db_path = HOSTFN("getDbPath", 2) {
- std::string db_name = args[0].asString(rt).utf8(rt);
- std::string path = std::string(basePath);
- if (count > 1 && !args[1].isUndefined() && !args[1].isNull()) {
- if (!args[1].isString()) {
- throw std::runtime_error(
- "[op-sqlite][open] database location must be a string");
- }
+ std::string db_name = args[0].asString(rt).utf8(rt);
+ std::string path = std::string(basePath);
+ if (count > 1 && !args[1].isUndefined() && !args[1].isNull()) {
+ if (!args[1].isString()) {
+ throw std::runtime_error(
+ "[op-sqlite][open] database location must be a string");
+ }
- std::string lastPath = args[1].asString(rt).utf8(rt);
+ std::string lastPath = args[1].asString(rt).utf8(rt);
- if (lastPath == ":memory:") {
- path = ":memory:";
- } else if (lastPath.rfind("/", 0) == 0) {
- path = lastPath;
- } else {
- path = path + "/" + lastPath;
- }
- }
+ if (lastPath == ":memory:") {
+ path = ":memory:";
+ } else if (lastPath.rfind("/", 0) == 0) {
+ path = lastPath;
+ } else {
+ path = path + "/" + lastPath;
+ }
+ }
- auto result = opsqlite_get_db_path(db_name, path);
- return jsi::String::createFromUtf8(rt, result);
+ auto result = opsqlite_get_db_path(db_name, path);
+ return jsi::String::createFromUtf8(rt, result);
});
jsi::Object module = jsi::Object(rt);
@@ -679,6 +561,10 @@ void install(jsi::Runtime &rt,
module.setProperty(rt, "executeRawAsync", std::move(execute_raw_async));
module.setProperty(rt, "getDbPath", std::move(get_db_path));
+ auto dbHostObj = std::make_shared();
+ module.setProperty(rt, "dbHostObject",
+ jsi::Object::createFromHostObject(rt, dbHostObj));
+
rt.global().setProperty(rt, "__OPSQLiteProxy", std::move(module));
}
diff --git a/cpp/sqlite3.h b/cpp/sqlite3.h
index 6f5dff13..0a83ecb7 100644
--- a/cpp/sqlite3.h
+++ b/cpp/sqlite3.h
@@ -6031,7 +6031,7 @@ SQLITE_API int sqlite3_set_clientdata(sqlite3 *, const char *, void *,
*/
typedef void (*sqlite3_destructor_type)(void *);
#define SQLITE_STATIC ((sqlite3_destructor_type)0)
-#define SQLITE_TRANSIENT ((sqlite3_destructor_type) - 1)
+#define SQLITE_TRANSIENT ((sqlite3_destructor_type)-1)
/*
** CAPI3REF: Setting The Result Of An SQL Function
diff --git a/cpp/validators/DbAttachValidator.cpp b/cpp/validators/DbAttachValidator.cpp
new file mode 100644
index 00000000..d4aa8de8
--- /dev/null
+++ b/cpp/validators/DbAttachValidator.cpp
@@ -0,0 +1,39 @@
+//
+// Created by jplc on 4/4/24.
+//
+
+#include "DbAttachValidator.h"
+
+namespace opsqlite {
+namespace validators {
+
+bool DbAttachValidator::invalidArgsNumber(std::string &msg, int count) {
+ if (count < 3) {
+ msg = "[op-sqlite][attach] Incorrect number of arguments";
+ return true;
+ }
+ return false;
+}
+
+bool DbAttachValidator::noStringArgs(std::string &msg, const jsi::Value &arg0, const jsi::Value &arg1, const jsi::Value &arg2) {
+ if (!arg0.isString() || !arg1.isString() || !arg2.isString()) {
+ msg = "dbName, databaseToAttach and alias must be a strings";
+ return true;
+ }
+ return false;
+}
+
+bool DbAttachValidator::locationArgDefined(const int count, const jsi::Value &arg3) {
+ return count > 3 && !arg3.isUndefined() && !arg3.isNull();
+}
+
+bool DbAttachValidator::locationArgIsNotString(std::string &msg, const jsi::Value &arg3) {
+ if (!arg3.isString()) {
+ msg = "[op-sqlite][attach] database location must be a string";
+ return true;
+ }
+ return false;
+}
+
+}
+}
\ No newline at end of file
diff --git a/cpp/validators/DbAttachValidator.h b/cpp/validators/DbAttachValidator.h
new file mode 100644
index 00000000..910a20a2
--- /dev/null
+++ b/cpp/validators/DbAttachValidator.h
@@ -0,0 +1,30 @@
+//
+// Created by jplc on 4/4/24.
+//
+
+#ifndef OPSQLITEEXAMPLE_VALIDATORS_DBATTACHVALIDATOR_H
+#define OPSQLITEEXAMPLE_VALIDATORS_DBATTACHVALIDATOR_H
+
+#include
+#include
+
+namespace jsi = facebook::jsi;
+
+namespace opsqlite {
+namespace validators {
+
+class DbAttachValidator {
+public:
+ static bool invalidArgsNumber(std::string &msg, int count);
+ static bool noStringArgs(std::string &msg, const jsi::Value &arg0, const jsi::Value &arg1, const jsi::Value &arg2);
+ static bool locationArgDefined(const int count, const jsi::Value &arg3);
+ static bool locationArgIsNotString(std::string &msg, const jsi::Value &arg3);
+
+private:
+ DbAttachValidator();
+};
+
+}
+}
+
+#endif //OPSQLITEEXAMPLE_VALIDATORS_DBATTACHVALIDATOR_H
diff --git a/cpp/validators/DbCloseValidator.cpp b/cpp/validators/DbCloseValidator.cpp
new file mode 100644
index 00000000..c7ef605a
--- /dev/null
+++ b/cpp/validators/DbCloseValidator.cpp
@@ -0,0 +1,27 @@
+//
+// Created by jplc on 4/4/24.
+//
+
+#include "DbCloseValidator.h"
+
+namespace opsqlite {
+namespace validators {
+
+bool DbCloseValidator::invalidArgsNumber(std::string &msg, int count) {
+ if (count == 0) {
+ msg = "[op-sqlite][close] database name is required";
+ return true;
+ }
+ return false;
+}
+
+bool DbCloseValidator::noStringArgs(std::string &msg, const jsi::Value &arg0) {
+ if (!arg0.isString()) {
+ msg = "[op-sqlite][close] database name must be a string";
+ return true;
+ }
+ return false;
+}
+
+}
+}
\ No newline at end of file
diff --git a/cpp/validators/DbCloseValidator.h b/cpp/validators/DbCloseValidator.h
new file mode 100644
index 00000000..28f6bb7f
--- /dev/null
+++ b/cpp/validators/DbCloseValidator.h
@@ -0,0 +1,28 @@
+//
+// Created by jplc on 4/4/24.
+//
+
+#ifndef OPSQLITEEXAMPLE_VALIDATORS_DBCLOSEVALIDATOR_H
+#define OPSQLITEEXAMPLE_VALIDATORS_DBCLOSEVALIDATOR_H
+
+#include
+#include
+
+namespace jsi = facebook::jsi;
+
+namespace opsqlite {
+namespace validators {
+
+class DbCloseValidator {
+public:
+ static bool invalidArgsNumber(std::string &msg, int count);
+ static bool noStringArgs(std::string &msg, const jsi::Value &arg0);
+
+private:
+ DbCloseValidator();
+};
+
+}
+}
+
+#endif //OPSQLITEEXAMPLE_VALIDATORS_DBCLOSEVALIDATOR_H
diff --git a/cpp/validators/DbDetachValidator.cpp b/cpp/validators/DbDetachValidator.cpp
new file mode 100644
index 00000000..96a107a1
--- /dev/null
+++ b/cpp/validators/DbDetachValidator.cpp
@@ -0,0 +1,27 @@
+//
+// Created by jplc on 4/4/24.
+//
+
+#include "DbDetachValidator.h"
+
+namespace opsqlite {
+namespace validators {
+
+bool DbDetachValidator::invalidArgsNumber(std::string &msg, int count) {
+ if (count < 2) {
+ msg = "[op-sqlite][detach] Incorrect number of arguments";
+ return true;
+ }
+ return false;
+}
+
+bool DbDetachValidator::noStringArgs(std::string &msg, const jsi::Value &arg0, const jsi::Value &arg1) {
+ if (!arg0.isString() || !arg1.isString()) {
+ msg = "dbName, databaseToAttach and alias must be a strings";
+ return true;
+ }
+ return false;
+}
+
+}
+}
\ No newline at end of file
diff --git a/cpp/validators/DbDetachValidator.h b/cpp/validators/DbDetachValidator.h
new file mode 100644
index 00000000..0dd2ad17
--- /dev/null
+++ b/cpp/validators/DbDetachValidator.h
@@ -0,0 +1,28 @@
+//
+// Created by jplc on 4/4/24.
+//
+
+#ifndef OPSQLITEEXAMPLE_VALIDATORS_DBDETACHVALIDATOR_H
+#define OPSQLITEEXAMPLE_VALIDATORS_DBDETACHVALIDATOR_H
+
+#include
+#include
+
+namespace jsi = facebook::jsi;
+
+namespace opsqlite {
+namespace validators {
+
+class DbDetachValidator {
+public:
+ static bool invalidArgsNumber(std::string &msg, int count);
+ static bool noStringArgs(std::string &msg, const jsi::Value &arg0, const jsi::Value &arg1);
+
+private:
+ DbDetachValidator();
+};
+
+}
+}
+
+#endif //OPSQLITEEXAMPLE_VALIDATORS_DBDETACHVALIDATOR_H
diff --git a/cpp/validators/DbOpenValidator.cpp b/cpp/validators/DbOpenValidator.cpp
new file mode 100644
index 00000000..c92b3821
--- /dev/null
+++ b/cpp/validators/DbOpenValidator.cpp
@@ -0,0 +1,46 @@
+#include "DbOpenValidator.h"
+
+namespace opsqlite {
+namespace validators {
+
+bool DbOpenValidator::invalidArgsNumber(std::string &msg, int count) {
+ if (count == 0) {
+ msg = "[op-sqlite][open] database name is required";
+ return true;
+ }
+ return false;
+}
+
+std::string DbOpenValidator::getPath(jsi::Runtime &rt, const jsi::Object &options, const std::string &basePath) {
+ std::string path = basePath;
+ std::string location = getLocation(rt, options);
+ if (!location.empty()) {
+ if (location == ":memory:") {
+ path = ":memory:";
+ } else if (location.rfind("/", 0) == 0) {
+ path = location;
+ } else {
+ path = path + "/" + location;
+ }
+ }
+ return path;
+}
+
+std::string DbOpenValidator::getLocation(jsi::Runtime &rt, const jsi::Object &options) {
+ std::string location = "";
+ if (options.hasProperty(rt, "location")) {
+ location = options.getProperty(rt, "location").asString(rt).utf8(rt);
+ }
+ return location;
+}
+
+std::string DbOpenValidator::getEncryptionKey(jsi::Runtime &rt, const jsi::Object &options) {
+ std::string encryptionKey = "";
+ if (options.hasProperty(rt, "encryptionKey")) {
+ encryptionKey = options.getProperty(rt, "encryptionKey").asString(rt).utf8(rt);
+ }
+ return encryptionKey;
+}
+
+}
+}
\ No newline at end of file
diff --git a/cpp/validators/DbOpenValidator.h b/cpp/validators/DbOpenValidator.h
new file mode 100644
index 00000000..c201ce20
--- /dev/null
+++ b/cpp/validators/DbOpenValidator.h
@@ -0,0 +1,26 @@
+#ifndef OPSQLITEEXAMPLE_VALIDATORS_DBOPENVALIDATOR_H
+#define OPSQLITEEXAMPLE_VALIDATORS_DBOPENVALIDATOR_H
+
+#include
+#include
+
+namespace jsi = facebook::jsi;
+
+namespace opsqlite {
+namespace validators {
+
+class DbOpenValidator {
+public:
+ static bool invalidArgsNumber(std::string &msg, int count);
+ static std::string getPath(jsi::Runtime &rt, const jsi::Object &options, const std::string &basePath);
+ static std::string getLocation(jsi::Runtime &rt, const jsi::Object &options);
+ static std::string getEncryptionKey(jsi::Runtime &rt, const jsi::Object &options);
+
+private:
+ DbOpenValidator();
+};
+
+}
+}
+
+#endif // OPSQLITEEXAMPLE_VALIDATORS_DBOPENVALIDATOR_H
\ No newline at end of file
diff --git a/example/.yarn/install-state.gz b/example/.yarn/install-state.gz
index cf4bc1bd..2763d14f 100644
Binary files a/example/.yarn/install-state.gz and b/example/.yarn/install-state.gz differ
diff --git a/turbo.json b/turbo.json
index b3e6acb3..8741b865 100644
--- a/turbo.json
+++ b/turbo.json
@@ -8,6 +8,8 @@
"!android/build",
"src/*.ts",
"src/*.tsx",
+ "cpp/*.h",
+ "cpp/*.cpp",
"example/package.json",
"example/android",
"!example/android/.gradle",
@@ -20,6 +22,7 @@
"inputs": [
"package.json",
"*.podspec",
+ "cpp/**",
"ios",
"src/*.ts",
"src/*.tsx",