Skip to content

Commit 8672f0b

Browse files
committed
SQL Loader Feature
- This feature allows fast "import" of SQL Files directly from a absolute path, on the C++ code, avoiding parsing large jsons or files via JavaScript in order to dispach calls to library. Some tests with datasets ~120k rows shows an improvment of at least 35% over the bulk update feature (PR 24). - Small bugfix on the "rowsAffected" property, that was collecting all changes on database instance since its opening instead of the current statement.
1 parent 740f16c commit 8672f0b

5 files changed

Lines changed: 157 additions & 1 deletion

File tree

cpp/SequelResult.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,10 @@ struct SequelResult
2828
string message;
2929
jsi::Value value;
3030
};
31+
32+
struct SequelLiteralUpdateResult
33+
{
34+
ResultType type;
35+
string message;
36+
int affectedRows;
37+
};

cpp/react-native-quick-sqlite.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#include <vector>
1515
#include <iostream>
1616
#include <thread>
17+
#include <iostream>
18+
#include <fstream>
1719

1820
using namespace std;
1921
using namespace facebook;
@@ -177,6 +179,58 @@ void installSequel(jsi::Runtime &rt, const char *docPath)
177179
return move(result.value);
178180
});
179181

182+
auto loadSQLFile = jsi::Function::createFromHostFunction(
183+
rt,
184+
jsi::PropNameID::forAscii(rt, "sequel_loadSQLFile"),
185+
2,
186+
[](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value
187+
{
188+
const string dbName = args[0].asString(rt).utf8(rt);
189+
const string sqlFileName = args[1].asString(rt).utf8(rt);
190+
191+
string line;
192+
ifstream sqFile (sqlFileName);
193+
if (sqFile.is_open())
194+
{
195+
try {
196+
int affectedRows = 0;
197+
int commands = 0;
198+
sequel_execute_literal_update(dbName, "BEGIN TRANSACTION");
199+
while ( std::getline (sqFile, line, '\n') )
200+
{
201+
if (!line.empty()) {
202+
SequelLiteralUpdateResult result = sequel_execute_literal_update(dbName, line);
203+
if( result.type == SequelResultError ) {
204+
sequel_execute_literal_update(dbName, "ROLLBACK");
205+
auto res = jsi::Object(rt);
206+
res.setProperty(rt, "status", jsi::Value(1));
207+
res.setProperty(rt, "message", jsi::String::createFromUtf8(rt, result.message.c_str()));
208+
sqFile.close();
209+
return move(res);
210+
} else {
211+
affectedRows += result.affectedRows;
212+
commands++;
213+
}
214+
}
215+
}
216+
sqFile.close();
217+
sequel_execute_literal_update(dbName, "COMMIT");
218+
auto res = jsi::Object(rt);
219+
res.setProperty(rt, "status", jsi::Value(0));
220+
res.setProperty(rt, "rowsAffected", jsi::Value(affectedRows));
221+
res.setProperty(rt, "commands", jsi::Value(commands));
222+
return move(res);
223+
} catch (...) {
224+
sequel_execute_literal_update(dbName, "ROLLBACK");
225+
jsi::detail::throwJSError(rt, "Unexpected error, transaction was rolledback");
226+
return {};
227+
}
228+
} else {
229+
jsi::detail::throwJSError(rt, "Unable to open file");
230+
return {};
231+
}
232+
});
233+
180234
// Async Execute SQL
181235
// auto asyncExecSQL = jsi::Function::createFromHostFunction(
182236
// rt,
@@ -220,6 +274,7 @@ void installSequel(jsi::Runtime &rt, const char *docPath)
220274
module.setProperty(rt, "delete", move(remove));
221275

222276
module.setProperty(rt, "executeSql", move(execSQL));
277+
module.setProperty(rt, "loadSqlFile", move(loadSQLFile));
223278
// module.setProperty(rt, "backgroundExecuteSql", move(asyncExecSQL));
224279

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

cpp/sequel.cpp

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ SequelResult sequel_execute(jsi::Runtime &rt, string const dbName, string const
414414
jsi::Object res = jsi::Object(rt);
415415
res.setProperty(rt, "rows", move(rows));
416416

417-
int changedRowCount = sqlite3_total_changes(db);
417+
int changedRowCount = sqlite3_changes(db);
418418
res.setProperty(rt, "rowsAffected", jsi::Value(changedRowCount));
419419

420420
// row id has nothing to do with the actual uuid/id of the object, but internal row count
@@ -429,3 +429,79 @@ SequelResult sequel_execute(jsi::Runtime &rt, string const dbName, string const
429429
"",
430430
move(res)};
431431
}
432+
433+
SequelLiteralUpdateResult sequel_execute_literal_update(string const dbName, string const &query)
434+
{
435+
// Check if db connection is opened
436+
if (dbMap.count(dbName) == 0)
437+
{
438+
return {
439+
SequelResultError,
440+
"[react-native-quick-sqlite] Database not opened: " + dbName,
441+
0
442+
};
443+
}
444+
445+
sqlite3 *db = dbMap[dbName];
446+
447+
// SQLite statements need to be compiled before executed
448+
sqlite3_stmt *statement;
449+
450+
// Compile and move result into statement memory spot
451+
int statementStatus = sqlite3_prepare_v2(db, query.c_str(), -1, &statement, NULL);
452+
453+
if (statementStatus != SQLITE_OK) // statemnet is correct, bind the passed parameters
454+
{
455+
const char *message = sqlite3_errmsg(db);
456+
return {
457+
SequelResultError,
458+
"[react-native-quick-sqlite] SQL execution error: " + string(message),
459+
0
460+
};
461+
}
462+
463+
bool isConsuming = true;
464+
bool isFailed = false;
465+
466+
int result, i, count, column_type;
467+
string column_name;
468+
469+
while (isConsuming)
470+
{
471+
result = sqlite3_step(statement);
472+
473+
switch (result)
474+
{
475+
case SQLITE_ROW:
476+
isConsuming = true;
477+
break;
478+
479+
case SQLITE_DONE:
480+
isConsuming = false;
481+
break;
482+
483+
default:
484+
isFailed = true;
485+
isConsuming = false;
486+
}
487+
}
488+
489+
sqlite3_finalize(statement);
490+
491+
if (isFailed)
492+
{
493+
const char *message = sqlite3_errmsg(db);
494+
return {
495+
SequelResultError,
496+
"[react-native-quick-sqlite] SQL execution error: " + string(message),
497+
0
498+
};
499+
}
500+
501+
int changedRowCount = sqlite3_changes(db);
502+
return {
503+
SequelResultOk,
504+
"",
505+
changedRowCount
506+
};
507+
}

cpp/sequel.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,5 @@ SequelResult sequel_remove(string const dbName, string const docPath);
2424
//SequelResult sequel_attach(string const &dbName);
2525

2626
SequelResult sequel_execute(jsi::Runtime &rt, string const dbName, string const &query, jsi::Value const &params);
27+
28+
SequelLiteralUpdateResult sequel_execute_literal_update(string const dbName, string const &query);

src/index.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ interface QueryResult {
3131
item: (idx: number) => any;
3232
};
3333
}
34+
35+
interface BulkupdateResult {
36+
rowsAffected?: number;
37+
commands?: number;
38+
message?: string;
39+
status?: 0 | 1;
40+
}
41+
3442
interface ISQLite {
3543
open: (dbName: string, location?: string) => any;
3644
close: (dbName: string) => any;
@@ -39,6 +47,7 @@ interface ISQLite {
3947
query: string,
4048
params: any[] | undefined
4149
) => QueryResult;
50+
loadSqlFile: (dbName: string, location: string) => BulkupdateResult;
4251
// backgroundExecuteSql: (dbName: string, query: string, params: any[]) => any;
4352
}
4453

@@ -58,6 +67,7 @@ interface IDBConnection {
5867
fail: (msg: string) => void
5968
) => void;
6069
close: (ok: (res: any) => void, fail: (msg: string) => void) => void;
70+
loadSqlFile: (location: string, callback: (result: BulkupdateResult) => void ) => void;
6171
}
6272

6373
export const openDatabase = (
@@ -97,6 +107,12 @@ export const openDatabase = (
97107
fail(e);
98108
}
99109
},
110+
loadSqlFile: (location: string, callback: (result: BulkupdateResult) => void) => {
111+
const result = sqlite.loadSqlFile(options.name, location);
112+
if (callback) {
113+
callback(result);
114+
}
115+
}
100116
};
101117

102118
ok(connection);

0 commit comments

Comments
 (0)