From c91bf89a60d1d9d9ed23b64c2caa0d039df437e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Fraz=C3=A3o?= Date: Mon, 14 Mar 2022 16:59:38 -0300 Subject: [PATCH 1/6] Refactory of importSQLFile - Method exported to its own file --- android/CMakeLists.txt | 2 ++ cpp/installer.cpp | 50 +------------------------------------- cpp/sqlfileloader.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++ cpp/sqlfileloader.h | 10 ++++++++ 4 files changed, 67 insertions(+), 49 deletions(-) create mode 100644 cpp/sqlfileloader.cpp create mode 100644 cpp/sqlfileloader.h diff --git a/android/CMakeLists.txt b/android/CMakeLists.txt index b9ca999..5c845be 100644 --- a/android/CMakeLists.txt +++ b/android/CMakeLists.txt @@ -49,6 +49,8 @@ add_library( ../cpp/JSIHelper.cpp ../cpp/ThreadPool.h ../cpp/ThreadPool.cpp + ../cpp/sqlfileloader.h + ../cpp/sqlfileloader.cpp cpp-adapter.cpp ) diff --git a/cpp/installer.cpp b/cpp/installer.cpp index 385fbf6..1973a0b 100644 --- a/cpp/installer.cpp +++ b/cpp/installer.cpp @@ -12,6 +12,7 @@ #include "logs.h" #include "JSIHelper.h" #include "ThreadPool.h" +#include "sqlfileloader.h" #include #include #include @@ -23,55 +24,6 @@ using namespace facebook; string docPathStr; std::shared_ptr invoker; -/** - * Local function to handle SQL File Import in order to reuse with Sync and Async operations - */ -SequelBatchOperationResult importSQLFile(string dbName, string fileLocation) -{ - string line; - ifstream sqFile(fileLocation); - if (sqFile.is_open()) - { - try - { - int affectedRows = 0; - int commands = 0; - sequel_execute_literal_update(dbName, "BEGIN EXCLUSIVE TRANSACTION"); - while (std::getline(sqFile, line, '\n')) - { - if (!line.empty()) - { - SequelLiteralUpdateResult result = sequel_execute_literal_update(dbName, line); - if (result.type == SequelResultError) - { - sequel_execute_literal_update(dbName, "ROLLBACK"); - sqFile.close(); - return {SequelResultError, result.message, 0, commands}; - } - else - { - affectedRows += result.affectedRows; - commands++; - } - } - } - sqFile.close(); - sequel_execute_literal_update(dbName, "COMMIT"); - return {SequelResultOk, "", affectedRows, commands}; - } - catch (...) - { - sqFile.close(); - sequel_execute_literal_update(dbName, "ROLLBACK"); - return {SequelResultError, "[react-native-quick-sqlite][loadSQLFile] Unexpected error, transaction was rolledback", 0, 0}; - } - } - else - { - return {SequelResultError, "[react-native-quick-sqlite][loadSQLFile] Could not open file", 0, 0}; - } -} - jsi::Object createError(jsi::Runtime &rt, string message) { auto res = jsi::Object(rt); diff --git a/cpp/sqlfileloader.cpp b/cpp/sqlfileloader.cpp new file mode 100644 index 0000000..da4cd8c --- /dev/null +++ b/cpp/sqlfileloader.cpp @@ -0,0 +1,54 @@ +/** + * SQL File Loader implementation +*/ +#include "sqlfileloader.h" +#include +#include + +using namespace std; + +SequelBatchOperationResult importSQLFile(string dbName, string fileLocation) +{ + string line; + ifstream sqFile(fileLocation); + if (sqFile.is_open()) + { + try + { + int affectedRows = 0; + int commands = 0; + sequel_execute_literal_update(dbName, "BEGIN EXCLUSIVE TRANSACTION"); + while (std::getline(sqFile, line, '\n')) + { + if (!line.empty()) + { + SequelLiteralUpdateResult result = sequel_execute_literal_update(dbName, line); + if (result.type == SequelResultError) + { + sequel_execute_literal_update(dbName, "ROLLBACK"); + sqFile.close(); + return {SequelResultError, result.message, 0, commands}; + } + else + { + affectedRows += result.affectedRows; + commands++; + } + } + } + sqFile.close(); + sequel_execute_literal_update(dbName, "COMMIT"); + return {SequelResultOk, "", affectedRows, commands}; + } + catch (...) + { + sqFile.close(); + sequel_execute_literal_update(dbName, "ROLLBACK"); + return {SequelResultError, "[react-native-quick-sqlite][loadSQLFile] Unexpected error, transaction was rolledback", 0, 0}; + } + } + else + { + return {SequelResultError, "[react-native-quick-sqlite][loadSQLFile] Could not open file", 0, 0}; + } +} diff --git a/cpp/sqlfileloader.h b/cpp/sqlfileloader.h new file mode 100644 index 0000000..c301035 --- /dev/null +++ b/cpp/sqlfileloader.h @@ -0,0 +1,10 @@ + +/** + * SQL File Loader + * Utilizes the regular sqlite bridge to load an SQLFile inside a transaction + * +*/ + +#include "JSIHelper.h" +#include "sqliteBridge.h" +SequelBatchOperationResult importSQLFile(std::string dbName, std::string fileLocation); \ No newline at end of file From 2196756b6aeea7114d7a29826f19f872cb42a15c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Fraz=C3=A3o?= Date: Mon, 14 Mar 2022 17:04:57 -0300 Subject: [PATCH 2/6] Removed unused imports --- cpp/installer.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/cpp/installer.cpp b/cpp/installer.cpp index 1973a0b..875d8d5 100644 --- a/cpp/installer.cpp +++ b/cpp/installer.cpp @@ -14,8 +14,6 @@ #include "ThreadPool.h" #include "sqlfileloader.h" #include -#include -#include #include using namespace std; From 273f6953f6d02503aebbbbbf858c5bb7a9f7ac62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Fraz=C3=A3o?= Date: Mon, 14 Mar 2022 19:35:14 -0300 Subject: [PATCH 3/6] Refactory of batchExecution - Exported to this own file - Created the async version of this feature --- android/CMakeLists.txt | 2 + cpp/installer.cpp | 126 ++++++++++++++++++++------------------- cpp/sqlbatchexecutor.cpp | 89 +++++++++++++++++++++++++++ cpp/sqlbatchexecutor.h | 24 ++++++++ cpp/sqliteBridge.cpp | 5 ++ package.json | 3 + src/index.ts | 15 +++++ 7 files changed, 204 insertions(+), 60 deletions(-) create mode 100644 cpp/sqlbatchexecutor.cpp create mode 100644 cpp/sqlbatchexecutor.h diff --git a/android/CMakeLists.txt b/android/CMakeLists.txt index 5c845be..a91a4db 100644 --- a/android/CMakeLists.txt +++ b/android/CMakeLists.txt @@ -51,6 +51,8 @@ add_library( ../cpp/ThreadPool.cpp ../cpp/sqlfileloader.h ../cpp/sqlfileloader.cpp + ../cpp/sqlbatchexecutor.h + ../cpp/sqlbatchexecutor.cpp cpp-adapter.cpp ) diff --git a/cpp/installer.cpp b/cpp/installer.cpp index 875d8d5..b1a4734 100644 --- a/cpp/installer.cpp +++ b/cpp/installer.cpp @@ -13,6 +13,7 @@ #include "JSIHelper.h" #include "ThreadPool.h" #include "sqlfileloader.h" +#include "sqlbatchexecutor.h" #include #include @@ -185,7 +186,7 @@ void install(jsi::Runtime &rt, std::shared_ptr jsCallInvoker // Converting results into a JSI Response auto jsiResult = createSequelQueryExecutionResult(rt, status, &results); - return jsiResult; + return move(jsiResult); }); // Execute a batch of SQL queries in a transaction @@ -201,80 +202,84 @@ void install(jsi::Runtime &rt, std::shared_ptr jsCallInvoker 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 == SequelResultOk) + { + 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) + { + 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"); } - int rowsAffected = 0; + + const string dbName = args[0].asString(rt).utf8(rt); const jsi::Array &batchParams = params.asObject(rt).asArray(rt); - try + auto callback = make_shared((args[2].asObject(rt))); + + vector commands; + jsiBatchParametersToQuickArguments(rt, batchParams, &commands); + + auto task = + [&rt, dbName, commands = make_shared>(commands), callback]() { - sequel_execute(rt, dbName, "BEGIN TRANSACTION", jsi::Value::undefined()); - for (int i = 0; i < batchParams.length(rt); i++) + try { - const jsi::Array &command = batchParams.getValueAtIndex(rt, i).asObject(rt).asArray(rt); - if (command.length(rt) == 0) - { - sequel_execute(rt, dbName, "ROLLBACK", jsi::Value::undefined()); - return createError(rt, "[react-native-quick-sqlite][execSQLBatch] - No SQL Commands found on batch index " + std::to_string(i)); - } - const string query = command.getValueAtIndex(rt, 0).asString(rt).utf8(rt); - const jsi::Value &commandParams = command.length(rt) > 1 ? command.getValueAtIndex(rt, 1) : jsi::Value::undefined(); - if (!commandParams.isUndefined() && commandParams.asObject(rt).isArray(rt) && commandParams.asObject(rt).asArray(rt).length(rt) > 0 && commandParams.asObject(rt).asArray(rt).getValueAtIndex(rt, 0).isObject()) - { - // This arguments are an array of arrays, like a batch update of a single sql command. - const jsi::Array &batchUpdateParams = commandParams.asObject(rt).asArray(rt); - for (int x = 0; x < batchUpdateParams.length(rt); x++) - { - const jsi::Value &p = batchUpdateParams.getValueAtIndex(rt, x); - SequelResult result = sequel_execute(rt, dbName, query, p); - if (result.type == SequelResultError) - { - sequel_execute(rt, dbName, "ROLLBACK", jsi::Value::undefined()); - return createError(rt, result.message.c_str()); - } - else - { - if (result.value.getObject(rt).hasProperty(rt, jsi::PropNameID::forAscii(rt, "rowsAffected"))) - { - rowsAffected += result.value.getObject(rt).getProperty(rt, jsi::PropNameID::forAscii(rt, "rowsAffected")).asNumber(); - } - } - } - } - else + // Inside the new worker thread, we can now call sqlite operations + auto batchResult = executeBatch(dbName, commands.get()); + invoker->invokeAsync([&rt, batchResult = move(batchResult), callback] { - SequelResult result = sequel_execute(rt, dbName, query, commandParams); - if (result.type == SequelResultError) + if(batchResult.type == SequelResultOk) { - sequel_execute(rt, dbName, "ROLLBACK", jsi::Value::undefined()); - - return createError(rt, result.message.c_str()); - } - else + 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)); + return move(res); + } else { - if (result.value.getObject(rt).hasProperty(rt, jsi::PropNameID::forAscii(rt, "rowsAffected"))) - { - rowsAffected += result.value.getObject(rt).getProperty(rt, jsi::PropNameID::forAscii(rt, "rowsAffected")).asNumber(); - } + callback->asObject(rt).asFunction(rt).call(rt, createError(rt, batchResult.message)); } - } + }); } - sequel_execute(rt, dbName, "COMMIT", jsi::Value::undefined()); - } - catch (...) - { - sequel_execute(rt, dbName, "ROLLBACK", jsi::Value::undefined()); - return createError(rt, "[react-native-quick-sqlite][execSQLBatch] - Unexpected error"); - } - - auto res = jsi::Object(rt); - res.setProperty(rt, "status", jsi::Value(0)); - res.setProperty(rt, "rowsAffected", jsi::Value(rowsAffected)); - return move(res); + 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 @@ -406,6 +411,7 @@ void install(jsi::Runtime &rt, std::shared_ptr jsCallInvoker 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)); diff --git a/cpp/sqlbatchexecutor.cpp b/cpp/sqlbatchexecutor.cpp new file mode 100644 index 0000000..8e1ce67 --- /dev/null +++ b/cpp/sqlbatchexecutor.cpp @@ -0,0 +1,89 @@ +/** + * Batch execution implementation +*/ +#include "sqlbatchexecutor.h" + +void jsiBatchParametersToQuickArguments(jsi::Runtime &rt, jsi::Array const &batchParams, vector *commands) +{ + for (int i = 0; i < batchParams.length(rt); i++) + { + const jsi::Array &command = batchParams.getValueAtIndex(rt, i).asObject(rt).asArray(rt); + if (command.length(rt) == 0) + { + continue; + } + + const string query = command.getValueAtIndex(rt, 0).asString(rt).utf8(rt); + const jsi::Value &commandParams = command.length(rt) > 1 ? command.getValueAtIndex(rt, 1) : jsi::Value::undefined(); + if (!commandParams.isUndefined() && commandParams.asObject(rt).isArray(rt) && commandParams.asObject(rt).asArray(rt).length(rt) > 0 && commandParams.asObject(rt).asArray(rt).getValueAtIndex(rt, 0).isObject()) + { + // This arguments is an array of arrays, like a batch update of a single sql command. + const jsi::Array &batchUpdateParams = commandParams.asObject(rt).asArray(rt); + for (int x = 0; x < batchUpdateParams.length(rt); x++) + { + const jsi::Value &p = batchUpdateParams.getValueAtIndex(rt, x); + vector params; + jsiQueryArgumentsToSequelParam(rt, p, ¶ms); + commands->push_back(QuickQueryArguments{ + query, + make_shared>(params) + }); + } + } + else + { + vector params; + jsiQueryArgumentsToSequelParam(rt, commandParams, ¶ms); + commands->push_back(QuickQueryArguments{ + query, + make_shared>(params) + }); + } + } +} + +SequelBatchOperationResult executeBatch(std::string dbName, vector *commands) +{ + size_t commandCount = commands->size(); + if(commandCount <= 0) + { + return SequelBatchOperationResult { + .type = SequelResultError, + .message = "No SQL commands provided", + }; + } + + try + { + int affectedRows = 0; + sequel_execute_literal_update(dbName, "BEGIN EXCLUSIVE TRANSACTION"); + for(int i = 0; iat(i); + // We do not provide a datastructure to receive query data because we don't need/want to handle this results in a batch execution + auto result = sequel_execute3(dbName, command.sql, command.params.get(), NULL); + if(result.type == SequelResultError) + { + return SequelBatchOperationResult { + .type = SequelResultError, + .message = result.errorMessage, + }; + } else + { + affectedRows += result.rowsAffected; + } + } + sequel_execute_literal_update(dbName, "COMMIT"); + return SequelBatchOperationResult { + .type = SequelResultOk, + .affectedRows = affectedRows, + .commands = (int) commandCount, + }; + } catch(std::exception &exc) + { + sequel_execute_literal_update(dbName, "ROLLBACK"); + return SequelBatchOperationResult { + .type = SequelResultError, + .message = exc.what(), + }; + } +} diff --git a/cpp/sqlbatchexecutor.h b/cpp/sqlbatchexecutor.h new file mode 100644 index 0000000..400e9b7 --- /dev/null +++ b/cpp/sqlbatchexecutor.h @@ -0,0 +1,24 @@ +/** + * SQL Batch execution implementation using default sqliteBridge implementation +*/ +#include "JSIHelper.h" +#include "sqliteBridge.h" + +using namespace std; +using namespace facebook; + +struct QuickQueryArguments { + string sql; + shared_ptr> params; +}; + +/** + * Local Helper method to translate JSI objects QuickQueryArguments datastructure + * MUST be called in the JavaScript Thread +*/ +void jsiBatchParametersToQuickArguments(jsi::Runtime &rt, jsi::Array const &batchParams, vector *commands); + +/** + * Execute a batch of commands in a exclusive transaction +*/ +SequelBatchOperationResult executeBatch(std::string dbName, vector *commands); diff --git a/cpp/sqliteBridge.cpp b/cpp/sqliteBridge.cpp index 56889ea..e78d58d 100644 --- a/cpp/sqliteBridge.cpp +++ b/cpp/sqliteBridge.cpp @@ -334,6 +334,11 @@ SequelOperationStatus sequel_execute3(string const dbName, string const &query, switch (result) { case SQLITE_ROW: + if(results == NULL) + { + break; + } + i = 0; row = map(); count = sqlite3_column_count(statement); diff --git a/package.json b/package.json index 5cc52e7..a8f1a94 100644 --- a/package.json +++ b/package.json @@ -138,5 +138,8 @@ } ] ] + }, + "dependencies": { + "react-native-quick-sqlite": "file:///home/eduardo/Projetos/react/plugins/react-native-quick-sqlite" } } diff --git a/src/index.ts b/src/index.ts index bbfce05..3f897f2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -90,6 +90,11 @@ interface ISQLite { dbName: string, commands: SQLBatchParams[] ) => BatchQueryResult; + asyncExecuteSqlBatch: ( + dbName: string, + commands: SQLBatchParams[], + cb: (res: BatchQueryResult) => void + ) => void; loadSqlFile: (dbName: string, location: string) => FileLoadResult; asyncLoadSqlFile: ( dbName: string, @@ -124,6 +129,10 @@ interface IDBConnection { commands: SQLBatchParams[], callback?: (res: BatchQueryResult) => void ) => void; + asyncExecuteSqlBatch: ( + commands: SQLBatchParams[], + cb: (res: BatchQueryResult) => void + ) => void; close: (ok: (res: any) => void, fail: (msg: string) => void) => void; loadSqlFile: ( location: string, @@ -189,6 +198,12 @@ export const openDatabase = ( const response = sqlite.executeSqlBatch(options.name, commands); if (callback) callback(response); }, + asyncExecuteSqlBatch: ( + commands: SQLBatchParams[], + cb: (res: BatchQueryResult) => void + ) => { + sqlite.asyncExecuteSqlBatch(options.name, commands, cb); + }, close: (ok: any, fail: any) => { try { sqlite.close(options.name); From 263de995c9c4c3b9af50886b801df768dbe463ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Fraz=C3=A3o?= Date: Mon, 14 Mar 2022 19:38:04 -0300 Subject: [PATCH 4/6] Removal of incorrect return statement --- cpp/installer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/cpp/installer.cpp b/cpp/installer.cpp index b1a4734..f7eda2b 100644 --- a/cpp/installer.cpp +++ b/cpp/installer.cpp @@ -265,7 +265,6 @@ void install(jsi::Runtime &rt, std::shared_ptr jsCallInvoker res.setProperty(rt, "status", jsi::Value(0)); res.setProperty(rt, "rowsAffected", jsi::Value(batchResult.affectedRows)); callback->asObject(rt).asFunction(rt).call(rt, move(res)); - return move(res); } else { callback->asObject(rt).asFunction(rt).call(rt, createError(rt, batchResult.message)); From 84e2e2d50a65afa7df54d87fe78eac5620e89295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Fraz=C3=A3o?= Date: Tue, 15 Mar 2022 08:47:25 -0300 Subject: [PATCH 5/6] Fixes - Incorrect dependency from package.json removed. - Better parameter checking of async batch execution --- cpp/installer.cpp | 13 +++++++++---- package.json | 3 --- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/cpp/installer.cpp b/cpp/installer.cpp index f7eda2b..8e797b1 100644 --- a/cpp/installer.cpp +++ b/cpp/installer.cpp @@ -233,19 +233,24 @@ void install(jsi::Runtime &rt, std::shared_ptr jsCallInvoker { if (sizeof(args) < 3) { - return createError(rt, "[react-native-quick-sqlite][execSQLBatch] - Incorrect parameter count"); + return {}; } const jsi::Value ¶ms = args[1]; + const jsi::Value &callbackHolder = args[2]; + if(!callbackHolder.isObject() || !callbackHolder.asObject(rt).isFunction(rt)) { + return {}; + } + if (params.isNull() || params.isUndefined()) { - return createError(rt, "[react-native-quick-sqlite][execSQLBatch] - An array of SQL commands or parameters is needed"); + callbackHolder.asObject(rt).asFunction(rt).call(rt, createError(rt, "[react-native-quick-sqlite][execSQLBatch] - 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((args[2].asObject(rt))); + auto callback = make_shared((callbackHolder.asObject(rt))); vector commands; jsiBatchParametersToQuickArguments(rt, batchParams, &commands); diff --git a/package.json b/package.json index a8f1a94..5cc52e7 100644 --- a/package.json +++ b/package.json @@ -138,8 +138,5 @@ } ] ] - }, - "dependencies": { - "react-native-quick-sqlite": "file:///home/eduardo/Projetos/react/plugins/react-native-quick-sqlite" } } From ea6de4fa652f507002de3735c698975dd8311168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Fraz=C3=A3o?= Date: Tue, 15 Mar 2022 09:43:45 -0300 Subject: [PATCH 6/6] New async methods now throw erros on parameters validation. --- cpp/installer.cpp | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/cpp/installer.cpp b/cpp/installer.cpp index 8e797b1..bc5dc3c 100644 --- a/cpp/installer.cpp +++ b/cpp/installer.cpp @@ -233,24 +233,26 @@ void install(jsi::Runtime &rt, std::shared_ptr jsCallInvoker { if (sizeof(args) < 3) { + jsi::detail::throwJSError(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)) { + jsi::detail::throwJSError(rt, "[react-native-quick-sqlite][asyncExecuteSqlBatch] The callback argument must be a function"); return {}; } if (params.isNull() || params.isUndefined()) { - callbackHolder.asObject(rt).asFunction(rt).call(rt, createError(rt, "[react-native-quick-sqlite][execSQLBatch] - An array of SQL commands or parameters is needed")); + jsi::detail::throwJSError(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))); + auto callback = make_shared(callbackHolder.asObject(rt)); vector commands; jsiBatchParametersToQuickArguments(rt, batchParams, &commands); @@ -318,9 +320,21 @@ void install(jsi::Runtime &rt, std::shared_ptr jsCallInvoker 3, [pool](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value { + if (sizeof(args) < 3) + { + jsi::detail::throwJSError(rt, "[react-native-quick-sqlite][asyncLoadSqlFile] Incorrect parameter count"); + return {}; + } + + const jsi::Value &callbackHolder = args[2]; + if(!callbackHolder.isObject() || !callbackHolder.asObject(rt).isFunction(rt)) { + jsi::detail::throwJSError(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((args[2].asObject(rt))); + auto callback = make_shared(callbackHolder.asObject(rt)); auto task = [&rt, dbName, sqlFileName, callback]() @@ -364,14 +378,20 @@ void install(jsi::Runtime &rt, std::shared_ptr jsCallInvoker { if (count < 4) { - jsi::detail::throwJSError(rt, "[react-native-quick-sqlite] Incorrect arguments for asyncExecuteSQL"); + jsi::detail::throwJSError(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)) { + jsi::detail::throwJSError(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(args[3].asObject(rt)); + auto callback = make_shared(callbackHolder.asObject(rt)); // Converting query parameters inside the javascript caller thread vector params;