1212#include " logs.h"
1313#include " JSIHelper.h"
1414#include " ThreadPool.h"
15+ #include " sqlfileloader.h"
16+ #include " sqlbatchexecutor.h"
1517#include < vector>
16- #include < iostream>
17- #include < fstream>
1818#include < string>
1919
2020using namespace std ;
@@ -23,55 +23,6 @@ using namespace facebook;
2323string docPathStr;
2424std::shared_ptr<react::CallInvoker> invoker;
2525
26- /* *
27- * Local function to handle SQL File Import in order to reuse with Sync and Async operations
28- */
29- SequelBatchOperationResult importSQLFile (string dbName, string fileLocation)
30- {
31- string line;
32- ifstream sqFile (fileLocation);
33- if (sqFile.is_open ())
34- {
35- try
36- {
37- int affectedRows = 0 ;
38- int commands = 0 ;
39- sequel_execute_literal_update (dbName, " BEGIN EXCLUSIVE TRANSACTION" );
40- while (std::getline (sqFile, line, ' \n ' ))
41- {
42- if (!line.empty ())
43- {
44- SequelLiteralUpdateResult result = sequel_execute_literal_update (dbName, line);
45- if (result.type == SequelResultError)
46- {
47- sequel_execute_literal_update (dbName, " ROLLBACK" );
48- sqFile.close ();
49- return {SequelResultError, result.message , 0 , commands};
50- }
51- else
52- {
53- affectedRows += result.affectedRows ;
54- commands++;
55- }
56- }
57- }
58- sqFile.close ();
59- sequel_execute_literal_update (dbName, " COMMIT" );
60- return {SequelResultOk, " " , affectedRows, commands};
61- }
62- catch (...)
63- {
64- sqFile.close ();
65- sequel_execute_literal_update (dbName, " ROLLBACK" );
66- return {SequelResultError, " [react-native-quick-sqlite][loadSQLFile] Unexpected error, transaction was rolledback" , 0 , 0 };
67- }
68- }
69- else
70- {
71- return {SequelResultError, " [react-native-quick-sqlite][loadSQLFile] Could not open file" , 0 , 0 };
72- }
73- }
74-
7526jsi::Object createError (jsi::Runtime &rt, string message)
7627{
7728 auto res = jsi::Object (rt);
@@ -235,7 +186,7 @@ void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker
235186
236187 // Converting results into a JSI Response
237188 auto jsiResult = createSequelQueryExecutionResult (rt, status, &results);
238- return jsiResult;
189+ return move ( jsiResult) ;
239190 });
240191
241192 // Execute a batch of SQL queries in a transaction
@@ -251,80 +202,90 @@ void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker
251202 return createError (rt, " [react-native-quick-sqlite][execSQLBatch] - Incorrect parameter count" );
252203 }
253204
254- const string dbName = args[0 ].asString (rt).utf8 (rt);
255205 const jsi::Value ¶ms = args[1 ];
256206 if (params.isNull () || params.isUndefined ())
257207 {
258208 return createError (rt, " [react-native-quick-sqlite][execSQLBatch] - An array of SQL commands or parameters is needed" );
259209 }
210+ const string dbName = args[0 ].asString (rt).utf8 (rt);
211+ const jsi::Array &batchParams = params.asObject (rt).asArray (rt);
212+ vector<QuickQueryArguments> commands;
213+ jsiBatchParametersToQuickArguments (rt, batchParams, &commands);
214+
215+ auto batchResult = executeBatch (dbName, &commands);
216+ if (batchResult.type == SequelResultOk)
217+ {
218+ auto res = jsi::Object (rt);
219+ res.setProperty (rt, " status" , jsi::Value (0 ));
220+ res.setProperty (rt, " rowsAffected" , jsi::Value (batchResult.affectedRows ));
221+ return move (res);
222+ } else
223+ {
224+ return createError (rt, batchResult.message );
225+ }
226+ });
227+
228+ auto execSQLBatchAsync = jsi::Function::createFromHostFunction (
229+ rt,
230+ jsi::PropNameID::forAscii (rt, " sequel_execSQLBatchAsync" ),
231+ 3 ,
232+ [pool](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value
233+ {
234+ if (sizeof (args) < 3 )
235+ {
236+ jsi::detail::throwJSError (rt, " [react-native-quick-sqlite][asyncExecuteSqlBatch] Incorrect parameter count" );
237+ return {};
238+ }
239+
240+ const jsi::Value ¶ms = args[1 ];
241+ const jsi::Value &callbackHolder = args[2 ];
242+ if (!callbackHolder.isObject () || !callbackHolder.asObject (rt).isFunction (rt)) {
243+ jsi::detail::throwJSError (rt, " [react-native-quick-sqlite][asyncExecuteSqlBatch] The callback argument must be a function" );
244+ return {};
245+ }
246+
247+ if (params.isNull () || params.isUndefined ())
248+ {
249+ jsi::detail::throwJSError (rt, " [react-native-quick-sqlite][asyncExecuteSqlBatch] - An array of SQL commands or parameters is needed" );
250+ return {};
251+ }
260252
261- int rowsAffected = 0 ;
253+ const string dbName = args[ 0 ]. asString (rt). utf8 (rt) ;
262254 const jsi::Array &batchParams = params.asObject (rt).asArray (rt);
263- try
255+ auto callback = make_shared<jsi::Value>(callbackHolder.asObject (rt));
256+
257+ vector<QuickQueryArguments> commands;
258+ jsiBatchParametersToQuickArguments (rt, batchParams, &commands);
259+
260+ auto task =
261+ [&rt, dbName, commands = make_shared<vector<QuickQueryArguments>>(commands), callback]()
264262 {
265- sequel_execute (rt, dbName, " BEGIN TRANSACTION" , jsi::Value::undefined ());
266- for (int i = 0 ; i < batchParams.length (rt); i++)
263+ try
267264 {
268- const jsi::Array &command = batchParams.getValueAtIndex (rt, i).asObject (rt).asArray (rt);
269- if (command.length (rt) == 0 )
270- {
271- sequel_execute (rt, dbName, " ROLLBACK" , jsi::Value::undefined ());
272- return createError (rt, " [react-native-quick-sqlite][execSQLBatch] - No SQL Commands found on batch index " + std::to_string (i));
273- }
274- const string query = command.getValueAtIndex (rt, 0 ).asString (rt).utf8 (rt);
275- const jsi::Value &commandParams = command.length (rt) > 1 ? command.getValueAtIndex (rt, 1 ) : jsi::Value::undefined ();
276- 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 ())
277- {
278- // This arguments are an array of arrays, like a batch update of a single sql command.
279- const jsi::Array &batchUpdateParams = commandParams.asObject (rt).asArray (rt);
280- for (int x = 0 ; x < batchUpdateParams.length (rt); x++)
281- {
282- const jsi::Value &p = batchUpdateParams.getValueAtIndex (rt, x);
283- SequelResult result = sequel_execute (rt, dbName, query, p);
284- if (result.type == SequelResultError)
285- {
286- sequel_execute (rt, dbName, " ROLLBACK" , jsi::Value::undefined ());
287- return createError (rt, result.message .c_str ());
288- }
289- else
290- {
291- if (result.value .getObject (rt).hasProperty (rt, jsi::PropNameID::forAscii (rt, " rowsAffected" )))
292- {
293- rowsAffected += result.value .getObject (rt).getProperty (rt, jsi::PropNameID::forAscii (rt, " rowsAffected" )).asNumber ();
294- }
295- }
296- }
297- }
298- else
265+ // Inside the new worker thread, we can now call sqlite operations
266+ auto batchResult = executeBatch (dbName, commands.get ());
267+ invoker->invokeAsync ([&rt, batchResult = move (batchResult), callback]
299268 {
300- SequelResult result = sequel_execute (rt, dbName, query, commandParams);
301- if (result.type == SequelResultError)
269+ if (batchResult.type == SequelResultOk)
302270 {
303- sequel_execute (rt, dbName, " ROLLBACK " , jsi::Value::undefined () );
304-
305- return createError (rt, result. message . c_str ( ));
306- }
307- else
271+ auto res = jsi::Object (rt );
272+ res. setProperty (rt, " status " , jsi::Value ( 0 ));
273+ res. setProperty (rt, " rowsAffected " , jsi::Value (batchResult. affectedRows ));
274+ callback-> asObject (rt). asFunction (rt). call (rt, move (res));
275+ } else
308276 {
309- if (result.value .getObject (rt).hasProperty (rt, jsi::PropNameID::forAscii (rt, " rowsAffected" )))
310- {
311- rowsAffected += result.value .getObject (rt).getProperty (rt, jsi::PropNameID::forAscii (rt, " rowsAffected" )).asNumber ();
312- }
277+ callback->asObject (rt).asFunction (rt).call (rt, createError (rt, batchResult.message ));
313278 }
314- }
279+ });
315280 }
316- sequel_execute (rt, dbName, " COMMIT" , jsi::Value::undefined ());
317- }
318- catch (...)
319- {
320- sequel_execute (rt, dbName, " ROLLBACK" , jsi::Value::undefined ());
321- return createError (rt, " [react-native-quick-sqlite][execSQLBatch] - Unexpected error" );
322- }
323-
324- auto res = jsi::Object (rt);
325- res.setProperty (rt, " status" , jsi::Value (0 ));
326- res.setProperty (rt, " rowsAffected" , jsi::Value (rowsAffected));
327- return move (res);
281+ catch (std::exception &exc)
282+ {
283+ invoker->invokeAsync ([&rt, callback, &exc]
284+ { callback->asObject (rt).asFunction (rt).call (rt, createError (rt, exc.what ())); });
285+ }
286+ };
287+ pool->queueWork (task);
288+ return {};
328289 });
329290
330291 // Load SQL File from disk
@@ -359,9 +320,21 @@ void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker
359320 3 ,
360321 [pool](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value
361322 {
323+ if (sizeof (args) < 3 )
324+ {
325+ jsi::detail::throwJSError (rt, " [react-native-quick-sqlite][asyncLoadSqlFile] Incorrect parameter count" );
326+ return {};
327+ }
328+
329+ const jsi::Value &callbackHolder = args[2 ];
330+ if (!callbackHolder.isObject () || !callbackHolder.asObject (rt).isFunction (rt)) {
331+ jsi::detail::throwJSError (rt, " [react-native-quick-sqlite][asyncLoadSqlFile] The callback argument must be a function" );
332+ return {};
333+ }
334+
362335 const string dbName = args[0 ].asString (rt).utf8 (rt);
363336 const string sqlFileName = args[1 ].asString (rt).utf8 (rt);
364- auto callback = make_shared<jsi::Value>((args[ 2 ] .asObject (rt) ));
337+ auto callback = make_shared<jsi::Value>(callbackHolder .asObject (rt));
365338
366339 auto task =
367340 [&rt, dbName, sqlFileName, callback]()
@@ -405,14 +378,20 @@ void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker
405378 {
406379 if (count < 4 )
407380 {
408- jsi::detail::throwJSError (rt, " [react-native-quick-sqlite] Incorrect arguments for asyncExecuteSQL" );
381+ jsi::detail::throwJSError (rt, " [react-native-quick-sqlite][asyncExecuteSql] Incorrect arguments for asyncExecuteSQL" );
382+ return {};
383+ }
384+
385+ const jsi::Value &callbackHolder = args[3 ];
386+ if (!callbackHolder.isObject () || !callbackHolder.asObject (rt).isFunction (rt)) {
387+ jsi::detail::throwJSError (rt, " [react-native-quick-sqlite][asyncExecuteSql] The callback argument must be a function" );
409388 return {};
410389 }
411390
412391 const string dbName = args[0 ].asString (rt).utf8 (rt);
413392 const string query = args[1 ].asString (rt).utf8 (rt);
414393 const jsi::Value &originalParams = args[2 ];
415- auto callback = make_shared<jsi::Value>(args[ 3 ] .asObject (rt));
394+ auto callback = make_shared<jsi::Value>(callbackHolder .asObject (rt));
416395
417396 // Converting query parameters inside the javascript caller thread
418397 vector<QuickValue> params;
@@ -456,6 +435,7 @@ void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker
456435 module .setProperty (rt, " executeSql" , move (execSQL));
457436 module .setProperty (rt, " asyncExecuteSql" , move (asyncExecSQL));
458437 module .setProperty (rt, " executeSqlBatch" , move (execSQLBatch));
438+ module .setProperty (rt, " asyncExecuteSqlBatch" , move (execSQLBatchAsync));
459439 module .setProperty (rt, " loadSqlFile" , move (loadSQLFile));
460440 module .setProperty (rt, " asyncLoadSqlFile" , move (loadSQLFileAsync));
461441
0 commit comments