From 41408af4cf803b489120b17c73cf9bf6a9a2d65d Mon Sep 17 00:00:00 2001 From: Oscar Franco Date: Sat, 1 Oct 2022 07:16:45 -0400 Subject: [PATCH] Replace macros --- README.md | 7 +- cpp/bindings.cpp | 445 ++++++++++++++++ cpp/{installer.h => bindings.h} | 12 +- cpp/installer.cpp | 502 ------------------ cpp/macros.h | 16 + example/ios/Podfile.lock | 8 + .../SequelExample.xcodeproj/project.pbxproj | 19 - ios/QuickSQLite.mm | 4 +- 8 files changed, 478 insertions(+), 535 deletions(-) create mode 100644 cpp/bindings.cpp rename cpp/{installer.h => bindings.h} (56%) delete mode 100644 cpp/installer.cpp create mode 100644 cpp/macros.h diff --git a/README.md b/README.md index 8fdcfe5..5330eeb 100644 --- a/README.md +++ b/README.md @@ -215,10 +215,11 @@ Batch execution allows transactional execution of a set of commands ```typescript const commands = [ ['CREATE TABLE TEST (id integer)'], - ['INSERT INTO TEST (id) VALUES (?)', [1]][ - ('INSERT INTO TEST (id) VALUES (?)', [2]) - ][('INSERT INTO TEST (id) VALUES (?)', [[3], [4], [5], [6]])], + ['INSERT INTO TEST (id) VALUES (?)', [1]], + [('INSERT INTO TEST (id) VALUES (?)', [2])], + [('INSERT INTO TEST (id) VALUES (?)', [[3], [4], [5], [6]])], ]; + const result = QuickSQLite.executeSqlBatch('myDatabase', commands); if (!result.status) { // result.status undefined or 0 === success diff --git a/cpp/bindings.cpp b/cpp/bindings.cpp new file mode 100644 index 0000000..d4aee2a --- /dev/null +++ b/cpp/bindings.cpp @@ -0,0 +1,445 @@ +#include "bindings.h" +#include "sqliteBridge.h" +#include "logs.h" +#include "JSIHelper.h" +#include "ThreadPool.h" +#include "sqlfileloader.h" +#include "sqlbatchexecutor.h" +#include +#include +#include "macros.h" + +using namespace std; +using namespace facebook; + +namespace osp { +string docPathStr; +std::shared_ptr invoker; + +jsi::Object createError(jsi::Runtime &rt, string message) +{ + auto res = jsi::Object(rt); + res.setProperty(rt, "status", jsi::Value(1)); + res.setProperty(rt, "message", jsi::String::createFromUtf8(rt, message)); + return res; +} + +jsi::Object createOk(jsi::Runtime &rt) +{ + auto res = jsi::Object(rt); + res.setProperty(rt, "status", jsi::Value(0)); + return res; +} + +void install(jsi::Runtime &rt, std::shared_ptr jsCallInvoker, const char *docPath) +{ + + // Transfer from pointer to variable to prevent de-allocation once calling function has finished + docPathStr = std::string(docPath); + auto pool = std::make_shared(); + invoker = jsCallInvoker; + + // Open/create db + auto open = HOSTFN("open", 2) { + if (count == 0) + { + return createError(rt, "[react-native-quick-sqlite][open] database name is required"); + } + + if (!args[0].isString()) + { + return createError(rt, "[react-native-quick-sqlite][open] database name must be a string"); + } + + string dbName = args[0].asString(rt).utf8(rt); + string tempDocPath = string(docPathStr); + if (count > 1 && !args[1].isUndefined() && !args[1].isNull()) + { + if (!args[1].isString()) + { + return createError(rt, "[react-native-quick-sqlite][open] database location must be a string"); + } + + tempDocPath = tempDocPath + "/" + args[1].asString(rt).utf8(rt); + } + + SQLiteOPResult result = sqliteOpenDb(dbName, tempDocPath); + + if (result.type == SQLiteError) + { + return createError(rt, result.errorMessage.c_str()); + } + + return createOk(rt); + }); + + auto attach = HOSTFN("attach", 4) { + if(count < 3) { + return createError(rt, "[react-native-quick-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 {}; + } + + string tempDocPath = string(docPathStr); + if (count > 3 && !args[3].isUndefined() && !args[3].isNull()) + { + if (!args[3].isString()) + { + return createError(rt, "[react-native-quick-sqlite][attach] database location must be a string"); + } + + tempDocPath = tempDocPath + "/" + args[3].asString(rt).utf8(rt); + } + + string dbName = args[0].asString(rt).utf8(rt); + string databaseToAttach = args[1].asString(rt).utf8(rt); + string alias = args[2].asString(rt).utf8(rt); + SQLiteOPResult result = sqliteAttachDb(dbName, tempDocPath, databaseToAttach, alias); + + if (result.type == SQLiteError) + { + return createError(rt, result.errorMessage.c_str()); + } + + return createOk(rt); + }); + + auto detach = HOSTFN("detach", 2) { + if(count < 2) { + return createError(rt, "[react-native-quick-sqlite][detach] Incorrect number of arguments"); + } + if (!args[0].isString() || !args[1].isString()) + { + throw jsi::JSError(rt, "dbName, databaseToAttach and alias must be a strings"); + return {}; + } + + string dbName = args[0].asString(rt).utf8(rt); + string alias = args[1].asString(rt).utf8(rt); + SQLiteOPResult result = sqliteDetachDb(dbName, alias); + + if (result.type == SQLiteError) + { + return createError(rt, result.errorMessage.c_str()); + } + + return createOk(rt); + }); + + auto close = HOSTFN("close", 1) + { + if (count == 0) + { + return createError(rt, "[react-native-quick-sqlite][close] database name is required"); + } + + if (!args[0].isString()) + { + return createError(rt, "[react-native-quick-sqlite][close] database name must be a string"); + } + + string dbName = args[0].asString(rt).utf8(rt); + + SQLiteOPResult result = sqliteCloseDb(dbName); + + if (result.type == SQLiteError) + { + return createError(rt, result.errorMessage.c_str()); + } + + return createOk(rt); + }); + + auto remove = HOSTFN("delete", 2) + { + if (count == 0) + { + return createError(rt, "[react-native-quick-sqlite][open] database name is required"); + } + + if (!args[0].isString()) + { + return createError(rt, "[react-native-quick-sqlite][open] database name must be a string"); + } + + string dbName = args[0].asString(rt).utf8(rt); + + string tempDocPath = string(docPathStr); + if (count > 1 && !args[1].isUndefined() && !args[1].isNull()) + { + if (!args[1].isString()) + { + return createError(rt, "[react-native-quick-sqlite][open] database location must be a string"); + } + + tempDocPath = tempDocPath + "/" + args[1].asString(rt).utf8(rt); + } + + + SQLiteOPResult result = sqliteRemoveDb(dbName, tempDocPath); + + if (result.type == SQLiteError) + { + return createError(rt, result.errorMessage.c_str()); + } + + return createOk(rt); + }); + + auto execSQL = HOSTFN("execute", 3) + { + const string dbName = args[0].asString(rt).utf8(rt); + const string query = args[1].asString(rt).utf8(rt); + const jsi::Value &originalParams = args[2]; + // Converting parameters + vector params; + jsiQueryArgumentsToSequelParam(rt, originalParams, ¶ms); + + // Filling the results + vector> results; + vector metadata; + auto status = sqliteExecute(dbName, query, ¶ms, &results, &metadata); + + // Converting results into a JSI Response + auto jsiResult = createSequelQueryExecutionResult(rt, status, &results, &metadata); + return jsiResult; + }); + + // Execute a batch of SQL queries in a transaction + // Parameters can be: [[sql: string, arguments: any[] | arguments: any[][] ]] + auto execSQLBatch = HOSTFN("executeBatch", 2) + { + if (sizeof(args) < 2) + { + return createError(rt, "[react-native-quick-sqlite][execSQLBatch] - Incorrect parameter count"); + } + + const jsi::Value ¶ms = args[1]; + if (params.isNull() || params.isUndefined()) + { + return createError(rt, "[react-native-quick-sqlite][execSQLBatch] - An array of SQL commands or parameters is needed"); + } + const string dbName = args[0].asString(rt).utf8(rt); + const jsi::Array &batchParams = params.asObject(rt).asArray(rt); + vector commands; + jsiBatchParametersToQuickArguments(rt, batchParams, &commands); + + auto batchResult = executeBatch(dbName, &commands); + if (batchResult.type == SQLiteOk) + { + auto res = jsi::Object(rt); + res.setProperty(rt, "status", jsi::Value(0)); + res.setProperty(rt, "rowsAffected", jsi::Value(batchResult.affectedRows)); + return move(res); + } + else + { + return createError(rt, batchResult.message); + } + }); + + auto execSQLBatchAsync = HOSTFN("executeBatchAsync", 3) + { + if (sizeof(args) < 3) + { + throw jsi::JSError(rt, "[react-native-quick-sqlite][asyncExecuteSqlBatch] Incorrect parameter count"); + return {}; + } + + const jsi::Value ¶ms = args[1]; + const jsi::Value &callbackHolder = args[2]; + if (!callbackHolder.isObject() || !callbackHolder.asObject(rt).isFunction(rt)) + { + throw jsi::JSError(rt, "[react-native-quick-sqlite][asyncExecuteSqlBatch] The callback argument must be a function"); + return {}; + } + + if (params.isNull() || params.isUndefined()) + { + throw jsi::JSError(rt, "[react-native-quick-sqlite][asyncExecuteSqlBatch] - An array of SQL commands or parameters is needed"); + return {}; + } + + const string dbName = args[0].asString(rt).utf8(rt); + const jsi::Array &batchParams = params.asObject(rt).asArray(rt); + auto callback = make_shared(callbackHolder.asObject(rt)); + + vector commands; + jsiBatchParametersToQuickArguments(rt, batchParams, &commands); + + auto task = + [&rt, dbName, commands = make_shared>(commands), callback]() + { + try + { + // Inside the new worker thread, we can now call sqlite operations + auto batchResult = executeBatch(dbName, commands.get()); + invoker->invokeAsync([&rt, batchResult = move(batchResult), callback] + { + if(batchResult.type == SQLiteOk) + { + auto res = jsi::Object(rt); + res.setProperty(rt, "status", jsi::Value(0)); + res.setProperty(rt, "rowsAffected", jsi::Value(batchResult.affectedRows)); + callback->asObject(rt).asFunction(rt).call(rt, move(res)); + } else + { + callback->asObject(rt).asFunction(rt).call(rt, createError(rt, batchResult.message)); + } }); + } + catch (std::exception &exc) + { + invoker->invokeAsync([&rt, callback, &exc] + { callback->asObject(rt).asFunction(rt).call(rt, createError(rt, exc.what())); }); + } + }; + pool->queueWork(task); + return {}; + }); + + auto loadSQLFile = HOSTFN("loadFile", 2) + { + const string dbName = args[0].asString(rt).utf8(rt); + const string sqlFileName = args[1].asString(rt).utf8(rt); + + const auto importResult = importSQLFile(dbName, sqlFileName); + if (importResult.type == SQLiteOk) + { + auto res = jsi::Object(rt); + res.setProperty(rt, "status", jsi::Value(0)); + res.setProperty(rt, "rowsAffected", jsi::Value(importResult.affectedRows)); + res.setProperty(rt, "commands", jsi::Value(importResult.commands)); + return move(res); + } + else + { + return createError(rt, "[react-native-quick-sqlite][loadSQLFile] Could not open file"); + } + }); + + // Load SQL File from disk in another thread + auto loadSQLFileAsync = HOSTFN("loadFileAsync", 3) + { + if (sizeof(args) < 3) + { + throw jsi::JSError(rt, "[react-native-quick-sqlite][asyncLoadSqlFile] Incorrect parameter count"); + return {}; + } + + const jsi::Value &callbackHolder = args[2]; + if (!callbackHolder.isObject() || !callbackHolder.asObject(rt).isFunction(rt)) + { + throw jsi::JSError(rt, "[react-native-quick-sqlite][asyncLoadSqlFile] The callback argument must be a function"); + return {}; + } + + const string dbName = args[0].asString(rt).utf8(rt); + const string sqlFileName = args[1].asString(rt).utf8(rt); + auto callback = make_shared(callbackHolder.asObject(rt)); + + auto task = + [&rt, dbName, sqlFileName, callback]() + { + try + { + // Running the import operation in another thread + const auto importResult = importSQLFile(dbName, sqlFileName); + + // Executing the callback invoke inside the JavaScript thread in order to safe build JSI objects that depends on jsi::Runtime and must be synchronized. + invoker->invokeAsync([&rt, result = move(importResult), callback] + { + if(result.type == SQLiteOk) + { + auto res = jsi::Object(rt); + res.setProperty(rt, "status", jsi::Value(0)); + res.setProperty(rt, "rowsAffected", jsi::Value(result.affectedRows)); + res.setProperty(rt, "commands", jsi::Value(result.commands)); + callback->asObject(rt).asFunction(rt).call(rt, move(res)); + } else { + callback->asObject(rt).asFunction(rt).call(rt, createError(rt, result.message)); + } }); + } + catch (std::exception &exc) + { + LOGW("Catched exception: %s", exc.what()); + invoker->invokeAsync([&rt, err = exc.what(), callback] + { callback->asObject(rt).asFunction(rt).call(rt, createError(rt, "Unknown error")); }); + } + }; + pool->queueWork(task); + return {}; + }); + + auto asyncExecSQL = HOSTFN("executeAsync", 4) + { + if (count < 4) + { + throw jsi::JSError(rt, "[react-native-quick-sqlite][asyncExecuteSql] Incorrect arguments for asyncExecuteSQL"); + return {}; + } + + const jsi::Value &callbackHolder = args[3]; + if (!callbackHolder.isObject() || !callbackHolder.asObject(rt).isFunction(rt)) + { + throw jsi::JSError(rt, "[react-native-quick-sqlite][asyncExecuteSql] The callback argument must be a function"); + return {}; + } + + const string dbName = args[0].asString(rt).utf8(rt); + const string query = args[1].asString(rt).utf8(rt); + const jsi::Value &originalParams = args[2]; + auto callback = make_shared(callbackHolder.asObject(rt)); + + // Converting query parameters inside the javascript caller thread + vector params; + jsiQueryArgumentsToSequelParam(rt, originalParams, ¶ms); + + auto task = + [&rt, dbName, query, params = make_shared>(params), callback]() + { + try + { + // Inside the new worker thread, we can now call sqlite operations + vector> results; + vector metadata; + auto status = sqliteExecute(dbName, query, params.get(), &results, &metadata); + invoker->invokeAsync([&rt, results = make_shared>>(results), metadata = make_shared>(metadata), status_copy = move(status), callback] + { + // Now, back into the JavaScript thread, we can translate the results + // back to a JSI Object to pass on the callback + auto jsiResult = createSequelQueryExecutionResult(rt, status_copy, results.get(), metadata.get()); + callback->asObject(rt).asFunction(rt).call(rt, move(jsiResult)); }); + } + catch (std::exception &exc) + { + invoker->invokeAsync([&rt, callback, &exc] + { callback->asObject(rt).asFunction(rt).call(rt, createError(rt, exc.what())); }); + } + }; + + pool->queueWork(task); + + return {}; + }); + + jsi::Object module = jsi::Object(rt); + + module.setProperty(rt, "open", move(open)); + module.setProperty(rt, "close", move(close)); + module.setProperty(rt, "attach", move(attach)); + module.setProperty(rt, "detach", move(detach)); + module.setProperty(rt, "delete", move(remove)); + module.setProperty(rt, "executeSql", move(execSQL)); + module.setProperty(rt, "asyncExecuteSql", move(asyncExecSQL)); + module.setProperty(rt, "executeSqlBatch", move(execSQLBatch)); + module.setProperty(rt, "asyncExecuteSqlBatch", move(execSQLBatchAsync)); + module.setProperty(rt, "loadSqlFile", move(loadSQLFile)); + module.setProperty(rt, "asyncLoadSqlFile", move(loadSQLFileAsync)); + + rt.global().setProperty(rt, "__QuickSQLiteProxy", move(module)); +} + +} diff --git a/cpp/installer.h b/cpp/bindings.h similarity index 56% rename from cpp/installer.h rename to cpp/bindings.h index 9cbc15c..d4b9be0 100644 --- a/cpp/installer.h +++ b/cpp/bindings.h @@ -1,16 +1,10 @@ -/* - * react-native-quick-sqlite.h - * - * Created by Oscar Franco on 2021/03/07 - * Copyright (c) 2021 Oscar Franco - * - * This code is licensed under the MIT license - */ - #include #include #include using namespace facebook; +namespace osp { void install(jsi::Runtime &rt, std::shared_ptr jsCallInvoker, const char *docPath); +} + diff --git a/cpp/installer.cpp b/cpp/installer.cpp deleted file mode 100644 index 1cb42ae..0000000 --- a/cpp/installer.cpp +++ /dev/null @@ -1,502 +0,0 @@ -/* - * react-native-quick-sqlite.cpp - * - * Created by Oscar Franco on 2021/03/07 - * Copyright (c) 2021 Oscar Franco - * - * This code is licensed under the MIT license - */ - -#include "installer.h" -#include "sqliteBridge.h" -#include "logs.h" -#include "JSIHelper.h" -#include "ThreadPool.h" -#include "sqlfileloader.h" -#include "sqlbatchexecutor.h" -#include -#include - -using namespace std; -using namespace facebook; - -string docPathStr; -std::shared_ptr invoker; - -jsi::Object createError(jsi::Runtime &rt, string message) -{ - auto res = jsi::Object(rt); - res.setProperty(rt, "status", jsi::Value(1)); - res.setProperty(rt, "message", jsi::String::createFromUtf8(rt, message)); - return res; -} - -jsi::Object createOk(jsi::Runtime &rt) -{ - auto res = jsi::Object(rt); - res.setProperty(rt, "status", jsi::Value(0)); - return res; -} - -void install(jsi::Runtime &rt, std::shared_ptr jsCallInvoker, const char *docPath) -{ - - // Transfer from pointer to variable to prevent de-allocation once calling function has finished - docPathStr = std::string(docPath); - auto pool = std::make_shared(); - invoker = jsCallInvoker; - - // Open/create db - auto open = jsi::Function::createFromHostFunction( - rt, - jsi::PropNameID::forAscii(rt, "sequel_open"), - 2, - [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value - { - if (count == 0) - { - return createError(rt, "[react-native-quick-sqlite][open] database name is required"); - } - - if (!args[0].isString()) - { - return createError(rt, "[react-native-quick-sqlite][open] database name must be a string"); - } - - string dbName = args[0].asString(rt).utf8(rt); - string tempDocPath = string(docPathStr); - if (count > 1 && !args[1].isUndefined() && !args[1].isNull()) - { - if (!args[1].isString()) - { - return createError(rt, "[react-native-quick-sqlite][open] database location must be a string"); - } - - tempDocPath = tempDocPath + "/" + args[1].asString(rt).utf8(rt); - } - - SQLiteOPResult result = sqliteOpenDb(dbName, tempDocPath); - - if (result.type == SQLiteError) - { - return createError(rt, result.errorMessage.c_str()); - } - - return createOk(rt); - }); - - auto attach = jsi::Function::createFromHostFunction( - rt, - jsi::PropNameID::forAscii(rt, "attach"), - 4, - [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value { - if(count < 3) { - return createError(rt, "[react-native-quick-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 {}; - } - - string tempDocPath = string(docPathStr); - if (count > 3 && !args[3].isUndefined() && !args[3].isNull()) - { - if (!args[3].isString()) - { - return createError(rt, "[react-native-quick-sqlite][attach] database location must be a string"); - } - - tempDocPath = tempDocPath + "/" + args[3].asString(rt).utf8(rt); - } - - string dbName = args[0].asString(rt).utf8(rt); - string databaseToAttach = args[1].asString(rt).utf8(rt); - string alias = args[2].asString(rt).utf8(rt); - SQLiteOPResult result = sqliteAttachDb(dbName, tempDocPath, databaseToAttach, alias); - - if (result.type == SQLiteError) - { - return createError(rt, result.errorMessage.c_str()); - } - - return createOk(rt); - }); - - auto detach = jsi::Function::createFromHostFunction( - rt, - jsi::PropNameID::forAscii(rt, "detach"), - 2, - [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value { - if(count < 2) { - return createError(rt, "[react-native-quick-sqlite][detach] Incorrect number of arguments"); - } - if (!args[0].isString() || !args[1].isString()) - { - throw jsi::JSError(rt, "dbName, databaseToAttach and alias must be a strings"); - return {}; - } - - string dbName = args[0].asString(rt).utf8(rt); - string alias = args[1].asString(rt).utf8(rt); - SQLiteOPResult result = sqliteDetachDb(dbName, alias); - - if (result.type == SQLiteError) - { - return createError(rt, result.errorMessage.c_str()); - } - - return createOk(rt); - }); - - // Close db - auto close = jsi::Function::createFromHostFunction( - rt, - jsi::PropNameID::forAscii(rt, "sequel_close"), - 1, - [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value - { - if (count == 0) - { - return createError(rt, "[react-native-quick-sqlite][close] database name is required"); - } - - if (!args[0].isString()) - { - return createError(rt, "[react-native-quick-sqlite][close] database name must be a string"); - } - - string dbName = args[0].asString(rt).utf8(rt); - - SQLiteOPResult result = sqliteCloseDb(dbName); - - if (result.type == SQLiteError) - { - return createError(rt, result.errorMessage.c_str()); - } - - return createOk(rt); - }); - - // Delete db - auto remove = jsi::Function::createFromHostFunction( - rt, - jsi::PropNameID::forAscii(rt, "sequel_delete"), - 2, - [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value - { - if (count == 0) - { - return createError(rt, "[react-native-quick-sqlite][open] database name is required"); - } - - if (!args[0].isString()) - { - return createError(rt, "[react-native-quick-sqlite][open] database name must be a string"); - } - - string dbName = args[0].asString(rt).utf8(rt); - - string tempDocPath = string(docPathStr); - if (count > 1 && !args[1].isUndefined() && !args[1].isNull()) - { - if (!args[1].isString()) - { - return createError(rt, "[react-native-quick-sqlite][open] database location must be a string"); - } - - tempDocPath = tempDocPath + "/" + args[1].asString(rt).utf8(rt); - } - - - SQLiteOPResult result = sqliteRemoveDb(dbName, tempDocPath); - - if (result.type == SQLiteError) - { - return createError(rt, result.errorMessage.c_str()); - } - - return createOk(rt); - }); - - // Execute SQL query - auto execSQL = jsi::Function::createFromHostFunction( - rt, - jsi::PropNameID::forAscii(rt, "sequel_execSQL"), - 3, - [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value - { - const string dbName = args[0].asString(rt).utf8(rt); - const string query = args[1].asString(rt).utf8(rt); - const jsi::Value &originalParams = args[2]; - // Converting parameters - vector params; - jsiQueryArgumentsToSequelParam(rt, originalParams, ¶ms); - - // Filling the results - vector> results; - vector metadata; - auto status = sqliteExecute(dbName, query, ¶ms, &results, &metadata); - - // Converting results into a JSI Response - auto jsiResult = createSequelQueryExecutionResult(rt, status, &results, &metadata); - return jsiResult; - }); - - // Execute a batch of SQL queries in a transaction - // Parameters can be: [[sql: string, arguments: any[] | arguments: any[][] ]] - auto execSQLBatch = jsi::Function::createFromHostFunction( - rt, - jsi::PropNameID::forAscii(rt, "sequel_execSQLBatch"), - 2, - [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value - { - if (sizeof(args) < 2) - { - return createError(rt, "[react-native-quick-sqlite][execSQLBatch] - Incorrect parameter count"); - } - - const jsi::Value ¶ms = args[1]; - if (params.isNull() || params.isUndefined()) - { - return createError(rt, "[react-native-quick-sqlite][execSQLBatch] - An array of SQL commands or parameters is needed"); - } - const string dbName = args[0].asString(rt).utf8(rt); - const jsi::Array &batchParams = params.asObject(rt).asArray(rt); - vector commands; - jsiBatchParametersToQuickArguments(rt, batchParams, &commands); - - auto batchResult = executeBatch(dbName, &commands); - if (batchResult.type == SQLiteOk) - { - auto res = jsi::Object(rt); - res.setProperty(rt, "status", jsi::Value(0)); - res.setProperty(rt, "rowsAffected", jsi::Value(batchResult.affectedRows)); - return move(res); - } - else - { - return createError(rt, batchResult.message); - } - }); - - auto execSQLBatchAsync = jsi::Function::createFromHostFunction( - rt, - jsi::PropNameID::forAscii(rt, "sequel_execSQLBatchAsync"), - 3, - [pool](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value - { - if (sizeof(args) < 3) - { - throw jsi::JSError(rt, "[react-native-quick-sqlite][asyncExecuteSqlBatch] Incorrect parameter count"); - return {}; - } - - const jsi::Value ¶ms = args[1]; - const jsi::Value &callbackHolder = args[2]; - if (!callbackHolder.isObject() || !callbackHolder.asObject(rt).isFunction(rt)) - { - throw jsi::JSError(rt, "[react-native-quick-sqlite][asyncExecuteSqlBatch] The callback argument must be a function"); - return {}; - } - - if (params.isNull() || params.isUndefined()) - { - throw jsi::JSError(rt, "[react-native-quick-sqlite][asyncExecuteSqlBatch] - An array of SQL commands or parameters is needed"); - return {}; - } - - const string dbName = args[0].asString(rt).utf8(rt); - const jsi::Array &batchParams = params.asObject(rt).asArray(rt); - auto callback = make_shared(callbackHolder.asObject(rt)); - - vector commands; - jsiBatchParametersToQuickArguments(rt, batchParams, &commands); - - auto task = - [&rt, dbName, commands = make_shared>(commands), callback]() - { - try - { - // Inside the new worker thread, we can now call sqlite operations - auto batchResult = executeBatch(dbName, commands.get()); - invoker->invokeAsync([&rt, batchResult = move(batchResult), callback] - { - if(batchResult.type == SQLiteOk) - { - auto res = jsi::Object(rt); - res.setProperty(rt, "status", jsi::Value(0)); - res.setProperty(rt, "rowsAffected", jsi::Value(batchResult.affectedRows)); - callback->asObject(rt).asFunction(rt).call(rt, move(res)); - } else - { - callback->asObject(rt).asFunction(rt).call(rt, createError(rt, batchResult.message)); - } }); - } - catch (std::exception &exc) - { - invoker->invokeAsync([&rt, callback, &exc] - { callback->asObject(rt).asFunction(rt).call(rt, createError(rt, exc.what())); }); - } - }; - pool->queueWork(task); - return {}; - }); - - // Load SQL File from disk - auto loadSQLFile = jsi::Function::createFromHostFunction( - rt, - jsi::PropNameID::forAscii(rt, "sequel_loadSQLFile"), - 2, - [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value - { - const string dbName = args[0].asString(rt).utf8(rt); - const string sqlFileName = args[1].asString(rt).utf8(rt); - - const auto importResult = importSQLFile(dbName, sqlFileName); - if (importResult.type == SQLiteOk) - { - auto res = jsi::Object(rt); - res.setProperty(rt, "status", jsi::Value(0)); - res.setProperty(rt, "rowsAffected", jsi::Value(importResult.affectedRows)); - res.setProperty(rt, "commands", jsi::Value(importResult.commands)); - return move(res); - } - else - { - return createError(rt, "[react-native-quick-sqlite][loadSQLFile] Could not open file"); - } - }); - - // Load SQL File from disk in another thread - auto loadSQLFileAsync = jsi::Function::createFromHostFunction( - rt, - jsi::PropNameID::forAscii(rt, "sequel_loadSQLFileAsync"), - 3, - [pool](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value - { - if (sizeof(args) < 3) - { - throw jsi::JSError(rt, "[react-native-quick-sqlite][asyncLoadSqlFile] Incorrect parameter count"); - return {}; - } - - const jsi::Value &callbackHolder = args[2]; - if (!callbackHolder.isObject() || !callbackHolder.asObject(rt).isFunction(rt)) - { - throw jsi::JSError(rt, "[react-native-quick-sqlite][asyncLoadSqlFile] The callback argument must be a function"); - return {}; - } - - const string dbName = args[0].asString(rt).utf8(rt); - const string sqlFileName = args[1].asString(rt).utf8(rt); - auto callback = make_shared(callbackHolder.asObject(rt)); - - auto task = - [&rt, dbName, sqlFileName, callback]() - { - try - { - // Running the import operation in another thread - const auto importResult = importSQLFile(dbName, sqlFileName); - - // Executing the callback invoke inside the JavaScript thread in order to safe build JSI objects that depends on jsi::Runtime and must be synchronized. - invoker->invokeAsync([&rt, result = move(importResult), callback] - { - if(result.type == SQLiteOk) - { - auto res = jsi::Object(rt); - res.setProperty(rt, "status", jsi::Value(0)); - res.setProperty(rt, "rowsAffected", jsi::Value(result.affectedRows)); - res.setProperty(rt, "commands", jsi::Value(result.commands)); - callback->asObject(rt).asFunction(rt).call(rt, move(res)); - } else { - callback->asObject(rt).asFunction(rt).call(rt, createError(rt, result.message)); - } }); - } - catch (std::exception &exc) - { - LOGW("Catched exception: %s", exc.what()); - invoker->invokeAsync([&rt, err = exc.what(), callback] - { callback->asObject(rt).asFunction(rt).call(rt, createError(rt, "Unknown error")); }); - } - }; - pool->queueWork(task); - return {}; - }); - - // Async Execute SQL - auto asyncExecSQL = jsi::Function::createFromHostFunction( - rt, - jsi::PropNameID::forAscii(rt, "sequel_asyncExecSQL"), - 4, - [pool](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value - { - if (count < 4) - { - throw jsi::JSError(rt, "[react-native-quick-sqlite][asyncExecuteSql] Incorrect arguments for asyncExecuteSQL"); - return {}; - } - - const jsi::Value &callbackHolder = args[3]; - if (!callbackHolder.isObject() || !callbackHolder.asObject(rt).isFunction(rt)) - { - throw jsi::JSError(rt, "[react-native-quick-sqlite][asyncExecuteSql] The callback argument must be a function"); - return {}; - } - - const string dbName = args[0].asString(rt).utf8(rt); - const string query = args[1].asString(rt).utf8(rt); - const jsi::Value &originalParams = args[2]; - auto callback = make_shared(callbackHolder.asObject(rt)); - - // Converting query parameters inside the javascript caller thread - vector params; - jsiQueryArgumentsToSequelParam(rt, originalParams, ¶ms); - - auto task = - [&rt, dbName, query, params = make_shared>(params), callback]() - { - try - { - // Inside the new worker thread, we can now call sqlite operations - vector> results; - vector metadata; - auto status = sqliteExecute(dbName, query, params.get(), &results, &metadata); - invoker->invokeAsync([&rt, results = make_shared>>(results), metadata = make_shared>(metadata), status_copy = move(status), callback] - { - // Now, back into the JavaScript thread, we can translate the results - // back to a JSI Object to pass on the callback - auto jsiResult = createSequelQueryExecutionResult(rt, status_copy, results.get(), metadata.get()); - callback->asObject(rt).asFunction(rt).call(rt, move(jsiResult)); }); - } - catch (std::exception &exc) - { - invoker->invokeAsync([&rt, callback, &exc] - { callback->asObject(rt).asFunction(rt).call(rt, createError(rt, exc.what())); }); - } - }; - - pool->queueWork(task); - - return {}; - }); - - // Global object - jsi::Object module = jsi::Object(rt); - - // Callable properties - module.setProperty(rt, "open", move(open)); - module.setProperty(rt, "close", move(close)); - module.setProperty(rt, "attach", move(attach)); - module.setProperty(rt, "detach", move(detach)); - module.setProperty(rt, "delete", move(remove)); - module.setProperty(rt, "executeSql", move(execSQL)); - module.setProperty(rt, "asyncExecuteSql", move(asyncExecSQL)); - module.setProperty(rt, "executeSqlBatch", move(execSQLBatch)); - module.setProperty(rt, "asyncExecuteSqlBatch", move(execSQLBatchAsync)); - module.setProperty(rt, "loadSqlFile", move(loadSQLFile)); - module.setProperty(rt, "asyncLoadSqlFile", move(loadSQLFileAsync)); - - rt.global().setProperty(rt, "__QuickSQLiteProxy", move(module)); -} diff --git a/cpp/macros.h b/cpp/macros.h new file mode 100644 index 0000000..c145707 --- /dev/null +++ b/cpp/macros.h @@ -0,0 +1,16 @@ +#ifndef macros_h +#define macros_h + +#define HOSTFN(name, basecount) \ +jsi::Function::createFromHostFunction( \ +rt, \ +jsi::PropNameID::forAscii(rt, name), \ +basecount, \ +[=](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value + +#define JSIFN(capture) \ +capture(jsi::Runtime &runtime, const jsi::Value &thisValue, \ +const jsi::Value *arguments, size_t count) \ +->jsi::Value + +#endif /* macros_h */ diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index f330693..7e998ed 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -228,6 +228,10 @@ PODS: - React-jsinspector (0.67.4) - React-logger (0.67.4): - glog + - react-native-quick-sqlite (4.0.5): + - React + - React-callinvoker + - React-Core - React-perflogger (0.67.4) - React-RCTActionSheet (0.67.4): - React-Core/RCTActionSheetHeaders (= 0.67.4) @@ -318,6 +322,7 @@ DEPENDENCIES: - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`) - React-logger (from `../node_modules/react-native/ReactCommon/logger`) + - react-native-quick-sqlite (from `../node_modules/react-native-quick-sqlite`) - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`) - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`) - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`) @@ -375,6 +380,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/jsinspector" React-logger: :path: "../node_modules/react-native/ReactCommon/logger" + react-native-quick-sqlite: + :path: "../node_modules/react-native-quick-sqlite" React-perflogger: :path: "../node_modules/react-native/ReactCommon/reactperflogger" React-RCTActionSheet: @@ -424,6 +431,7 @@ SPEC CHECKSUMS: React-jsiexecutor: cbdf37cebdc4f5d8b3d0bf5ccaa6147fd9de9f3d React-jsinspector: f4775ea9118cbe1f72b834f0f842baa7a99508d8 React-logger: a1f028f6d8639a3f364ef80419e5e862e1115250 + react-native-quick-sqlite: 69befc539ffb566f2efc82fedc95ca14e20300ef React-perflogger: 0afaf2f01a47fd0fc368a93bfbb5bd3b26db6e7f React-RCTActionSheet: 59f35c4029e0b532fc42114241a06e170b7431a2 React-RCTAnimation: aae4f4bed122e78bdab72f7118d291d70a932ce2 diff --git a/example/ios/SequelExample.xcodeproj/project.pbxproj b/example/ios/SequelExample.xcodeproj/project.pbxproj index 7653eb2..098558d 100644 --- a/example/ios/SequelExample.xcodeproj/project.pbxproj +++ b/example/ios/SequelExample.xcodeproj/project.pbxproj @@ -120,7 +120,6 @@ 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, C1D60D28B925C94BD88E79D7 /* [CP] Copy Pods Resources */, - 393A271D70248A9845098103 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -190,24 +189,6 @@ shellPath = /bin/sh; shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n"; }; - 393A271D70248A9845098103 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-SequelExample/Pods-SequelExample-frameworks.sh", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/hermes.framework/hermes", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SequelExample/Pods-SequelExample-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; 4F0A6FC082772762E3E4C96C /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; diff --git a/ios/QuickSQLite.mm b/ios/QuickSQLite.mm index 74cb12c..c2857a6 100644 --- a/ios/QuickSQLite.mm +++ b/ios/QuickSQLite.mm @@ -6,7 +6,7 @@ #import #import -#import "../cpp/installer.h" +#import "../cpp/bindings.h" @implementation QuickSQLite @@ -35,7 +35,7 @@ @implementation QuickSQLite NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true); NSString *documentPath = [paths objectAtIndex:0]; - install(runtime, callInvoker,[documentPath UTF8String]); + osp::install(runtime, callInvoker,[documentPath UTF8String]); return @true; }