Skip to content

Commit 64a2457

Browse files
committed
Added a native method to provide transactional batch execution.
The idea is to avoid to many JSI Calls for large operations.
1 parent 740f16c commit 64a2457

2 files changed

Lines changed: 113 additions & 0 deletions

File tree

cpp/react-native-quick-sqlite.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,88 @@ void installSequel(jsi::Runtime &rt, const char *docPath)
177177
return move(result.value);
178178
});
179179

180+
// Parameters can be: [[sql: string, arguments: any[] | arguments: any[][] ]]
181+
auto execSQLBatch = jsi::Function::createFromHostFunction(
182+
rt,
183+
jsi::PropNameID::forAscii(rt, "sequel_execSQLBatch"),
184+
2,
185+
[](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value
186+
{
187+
if(sizeof(args) < 2) {
188+
jsi::detail::throwJSError(rt, "[react-native-quick-sqlite][execSQLBatch] - Incorrect parameter count");
189+
return {};
190+
}
191+
const string dbName = args[0].asString(rt).utf8(rt);
192+
const jsi::Value &params = args[1];
193+
if(params.isNull() || params.isUndefined())
194+
{
195+
jsi::detail::throwJSError(rt, "[react-native-quick-sqlite][execSQLBatch] - An array of SQL commands or parameters is needed");
196+
return {};
197+
}
198+
int rowsAffected = 0;
199+
const jsi::Array &batchParams = params.asObject(rt).asArray(rt);
200+
try
201+
{
202+
sequel_execute(rt, dbName, "BEGIN TRANSACTION", jsi::Value::undefined());
203+
for(int i = 0; i<batchParams.length(rt); i++)
204+
{
205+
const jsi::Array &command = batchParams.getValueAtIndex(rt, i).asObject(rt).asArray(rt);
206+
if(command.length(rt) == 0) {
207+
sequel_execute(rt, dbName, "ROLLBACK", jsi::Value::undefined());
208+
jsi::detail::throwJSError(rt, "[react-native-quick-sqlite][execSQLBatch] - No SQL Commands found");
209+
return {};
210+
}
211+
const string query = command.getValueAtIndex(rt, 0).asString(rt).utf8(rt);
212+
const jsi::Value &commandParams = command.length(rt) > 1 ? command.getValueAtIndex(rt, 1) : jsi::Value::undefined();
213+
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())
214+
{
215+
// This arguments are an array of arrays, like a batch update of a single sql command.
216+
const jsi::Array &batchUpdateParams = commandParams.asObject(rt).asArray(rt);
217+
for(int x = 0; x<batchUpdateParams.length(rt); x++)
218+
{
219+
const jsi::Value &p = batchUpdateParams.getValueAtIndex(rt, x);
220+
SequelResult result = sequel_execute(rt, dbName, query, p);
221+
if (result.type == SequelResultError)
222+
{
223+
sequel_execute(rt, dbName, "ROLLBACK", jsi::Value::undefined());
224+
auto res = jsi::Object(rt);
225+
res.setProperty(rt, "status", jsi::Value(1));
226+
res.setProperty(rt, "message", jsi::String::createFromUtf8(rt, result.message.c_str()));
227+
return move(res);
228+
} else {
229+
if(result.value.getObject(rt).hasProperty(rt, jsi::PropNameID::forAscii(rt, "rowsAffected")))
230+
{
231+
rowsAffected += result.value.getObject(rt).getProperty(rt, jsi::PropNameID::forAscii(rt, "rowsAffected")).asNumber();
232+
}
233+
}
234+
}
235+
} else {
236+
SequelResult result = sequel_execute(rt, dbName, query, commandParams);
237+
if (result.type == SequelResultError)
238+
{
239+
sequel_execute(rt, dbName, "ROLLBACK", jsi::Value::undefined());
240+
auto res = jsi::Object(rt);
241+
res.setProperty(rt, "status", jsi::Value(1));
242+
res.setProperty(rt, "message", jsi::String::createFromUtf8(rt, result.message.c_str()));
243+
return move(res);
244+
} else {
245+
if(result.value.getObject(rt).hasProperty(rt, jsi::PropNameID::forAscii(rt, "rowsAffected")))
246+
{
247+
rowsAffected += result.value.getObject(rt).getProperty(rt, jsi::PropNameID::forAscii(rt, "rowsAffected")).asNumber();
248+
}
249+
}
250+
}
251+
}
252+
sequel_execute(rt, dbName, "COMMIT", jsi::Value::undefined());
253+
} catch (...) {
254+
sequel_execute(rt, dbName, "ROLLBACK", jsi::Value::undefined());
255+
}
256+
auto res = jsi::Object(rt);
257+
res.setProperty(rt, "status", jsi::Value(0));
258+
res.setProperty(rt, "rowsAffected", jsi::Value(rowsAffected));
259+
return move(res);
260+
});
261+
180262
// Async Execute SQL
181263
// auto asyncExecSQL = jsi::Function::createFromHostFunction(
182264
// rt,
@@ -220,6 +302,8 @@ void installSequel(jsi::Runtime &rt, const char *docPath)
220302
module.setProperty(rt, "delete", move(remove));
221303

222304
module.setProperty(rt, "executeSql", move(execSQL));
305+
module.setProperty(rt, "executeSqlBatch", move(execSQLBatch));
306+
223307
// module.setProperty(rt, "backgroundExecuteSql", move(asyncExecSQL));
224308

225309
rt.global().setProperty(rt, "sqlite", move(module));

src/index.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,26 @@ interface QueryResult {
3131
item: (idx: number) => any;
3232
};
3333
}
34+
35+
/**
36+
* Allows the execution of bulk of sql commands
37+
* inside a transaction
38+
* If a single query must be executed many times with different arguments, its preferred
39+
* to declare it a single time, and use an array of array parameters.
40+
*/
41+
type SQLBatchParams = [string, Array<any> | Array<Array<any>> | undefined];
42+
43+
/**
44+
* status: 0 or undefined for correct execution, 1 for error
45+
* message: if status === 1, here you will find error description
46+
* rowsAffected: Number of affected rows if status == 0
47+
*/
48+
interface BatchExecutionResult {
49+
status?: 0 | 1;
50+
rowsAffected?: number;
51+
message?: string;
52+
}
53+
3454
interface ISQLite {
3555
open: (dbName: string, location?: string) => any;
3656
close: (dbName: string) => any;
@@ -39,6 +59,10 @@ interface ISQLite {
3959
query: string,
4060
params: any[] | undefined
4161
) => QueryResult;
62+
executeSqlBatch: (
63+
dbName: string,
64+
commands: SQLBatchParams[]
65+
) => BatchExecutionResult;
4266
// backgroundExecuteSql: (dbName: string, query: string, params: any[]) => any;
4367
}
4468

@@ -57,6 +81,7 @@ interface IDBConnection {
5781
ok: (res: QueryResult) => void,
5882
fail: (msg: string) => void
5983
) => void;
84+
executeSqlBatch: (commands: SQLBatchParams[], callback?: (res: BatchExecutionResult) => void) => void;
6085
close: (ok: (res: any) => void, fail: (msg: string) => void) => void;
6186
}
6287

@@ -89,6 +114,10 @@ export const openDatabase = (
89114
fail(e);
90115
}
91116
},
117+
executeSqlBatch: (commands: SQLBatchParams[], callback?: (res: BatchExecutionResult) => void) => {
118+
const response = sqlite.executeSqlBatch(options.name, commands);
119+
if (callback) callback(response);
120+
},
92121
close: (ok: any, fail: any) => {
93122
try {
94123
sqlite.close(options.name);

0 commit comments

Comments
 (0)