@op-engineering/op-sqlite 2.0.7 → 2.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -1
- package/cpp/DumbHostObject.cpp +28 -0
- package/cpp/DumbHostObject.h +5 -0
- package/cpp/bindings.cpp +15 -15
- package/cpp/bridge.cpp +39 -88
- package/cpp/bridge.h +31 -31
- package/cpp/sqlbatchexecutor.cpp +6 -6
- package/cpp/utils.cpp +66 -5
- package/cpp/utils.h +8 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -119,10 +119,49 @@ const db = open({
|
|
|
119
119
|
db.execute('PRAGMA mmap_size=268435456');
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
You can also set journaling to memory (or even OFF if you are kinda crazy) to gain even more speed. Journaling is what allows SQLite to ROLLBACK statements and it is dangerous, so do it at your own risk
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
db.execute('PRAGMA journal_mode = MEMORY;'); // or OFF
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
If you use [prepared statements](#prepared-statements) plus memory mapping and set journaling to memory, you can get to inches of MMKV for the most performance critical queries, here is a simple example writing/reading a single value.
|
|
123
129
|
|
|
124
130
|

|
|
125
131
|
|
|
132
|
+
# SQLite Gotchas
|
|
133
|
+
|
|
134
|
+
## Strictness
|
|
135
|
+
|
|
136
|
+
It's important to notice SQLite unlike other databases by default does not strictly check for types, if you want true type safety when you declare your tables you need to use the `STRICT` keyword.
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
db.execute('CREATE TABLE Test (
|
|
140
|
+
id INT PRIMARY KEY,
|
|
141
|
+
name TEXT
|
|
142
|
+
) STRICT;');
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
If you don't set it, SQLite will happily write whatever you insert in your table, independtly of the declared type.
|
|
146
|
+
|
|
147
|
+
## Foreign constraints
|
|
148
|
+
|
|
149
|
+
When SQLite evaluates your query and you have forgein key constraints, it keeps track of the satisfied relations via a counter. Once your statement finishes executing and the counter is not 0, it throws a foreign key constraint failed error. Unfortunately, this simple design means it is impossible to catch which foreign constraint is failed and you will receive a generic error. Nothing op-sqlite can do about it, it's a design flaw in SQLite.
|
|
150
|
+
|
|
151
|
+
In order to catch foreign key errors, you also need to execute the pragma when you open your connection:
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
PRAGMA foreign_keys = true
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Error codes
|
|
158
|
+
|
|
159
|
+
Sometimes you might be using valid SQL syntax for other engines or you might be doing something else wrong. The errors returned by op-sqlite contain the raw error code returned by SQLite and you should check [the reference](https://www.sqlite.org/rescode.html) for more detailed information.
|
|
160
|
+
|
|
161
|
+
## Quirks
|
|
162
|
+
|
|
163
|
+
See the [full list of SQLite quirks](https://www.sqlite.org/quirks.html).
|
|
164
|
+
|
|
126
165
|
# API
|
|
127
166
|
|
|
128
167
|
```typescript
|
package/cpp/DumbHostObject.cpp
CHANGED
|
@@ -36,7 +36,35 @@ jsi::Value DumbHostObject::get(jsi::Runtime &rt,
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
for (auto pairField : ownValues) {
|
|
40
|
+
if (name == pairField.first) {
|
|
41
|
+
return toJSI(rt, pairField.second);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
39
45
|
return {};
|
|
40
46
|
}
|
|
41
47
|
|
|
48
|
+
void DumbHostObject::set(jsi::Runtime &rt, const jsi::PropNameID &name,
|
|
49
|
+
const jsi::Value &value) {
|
|
50
|
+
auto key = name.utf8(rt);
|
|
51
|
+
auto fields = metadata.get();
|
|
52
|
+
for (int i = 0; i < fields->size(); i++) {
|
|
53
|
+
auto fieldName = std::get<std::string>(fields->at(i).fields[0].second);
|
|
54
|
+
if (fieldName == key) {
|
|
55
|
+
values[i] = toVariant(rt, value);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
for (auto pairField : ownValues) {
|
|
61
|
+
if (key == pairField.first) {
|
|
62
|
+
pairField.second = toVariant(rt, value);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
ownValues.push_back(std::make_pair(key, toVariant(rt, value)));
|
|
68
|
+
}
|
|
69
|
+
|
|
42
70
|
} // namespace opsqlite
|
package/cpp/DumbHostObject.h
CHANGED
|
@@ -23,9 +23,14 @@ public:
|
|
|
23
23
|
|
|
24
24
|
jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &propNameID);
|
|
25
25
|
|
|
26
|
+
void set(jsi::Runtime &rt, const jsi::PropNameID &name,
|
|
27
|
+
const jsi::Value &value);
|
|
28
|
+
|
|
26
29
|
std::vector<JSVariant> values;
|
|
27
30
|
|
|
28
31
|
std::shared_ptr<std::vector<SmartHostObject>> metadata;
|
|
32
|
+
|
|
33
|
+
std::vector<std::pair<std::string, JSVariant>> ownValues;
|
|
29
34
|
};
|
|
30
35
|
|
|
31
36
|
} // namespace opsqlite
|
package/cpp/bindings.cpp
CHANGED
|
@@ -35,7 +35,7 @@ bool invalidated = false;
|
|
|
35
35
|
void clearState() {
|
|
36
36
|
invalidated = true;
|
|
37
37
|
// Will terminate all operations and database connections
|
|
38
|
-
|
|
38
|
+
sqlite_close_all();
|
|
39
39
|
// We then join all the threads before the context gets invalidated
|
|
40
40
|
pool.restartPool();
|
|
41
41
|
|
|
@@ -81,7 +81,7 @@ void install(jsi::Runtime &rt,
|
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
BridgeResult result =
|
|
84
|
+
BridgeResult result = sqlite_open(dbName, path);
|
|
85
85
|
|
|
86
86
|
if (result.type == SQLiteError) {
|
|
87
87
|
throw std::runtime_error(result.message);
|
|
@@ -115,7 +115,7 @@ void install(jsi::Runtime &rt,
|
|
|
115
115
|
std::string databaseToAttach = args[1].asString(rt).utf8(rt);
|
|
116
116
|
std::string alias = args[2].asString(rt).utf8(rt);
|
|
117
117
|
BridgeResult result =
|
|
118
|
-
|
|
118
|
+
sqlite_attach(dbName, tempDocPath, databaseToAttach, alias);
|
|
119
119
|
|
|
120
120
|
if (result.type == SQLiteError) {
|
|
121
121
|
throw std::runtime_error(result.message);
|
|
@@ -137,7 +137,7 @@ void install(jsi::Runtime &rt,
|
|
|
137
137
|
|
|
138
138
|
std::string dbName = args[0].asString(rt).utf8(rt);
|
|
139
139
|
std::string alias = args[1].asString(rt).utf8(rt);
|
|
140
|
-
BridgeResult result =
|
|
140
|
+
BridgeResult result = sqlite_detach(dbName, alias);
|
|
141
141
|
|
|
142
142
|
if (result.type == SQLiteError) {
|
|
143
143
|
throw jsi::JSError(rt, result.message.c_str());
|
|
@@ -158,7 +158,7 @@ void install(jsi::Runtime &rt,
|
|
|
158
158
|
|
|
159
159
|
std::string dbName = args[0].asString(rt).utf8(rt);
|
|
160
160
|
|
|
161
|
-
BridgeResult result =
|
|
161
|
+
BridgeResult result = sqlite_close(dbName);
|
|
162
162
|
|
|
163
163
|
if (result.type == SQLiteError) {
|
|
164
164
|
throw jsi::JSError(rt, result.message.c_str());
|
|
@@ -190,7 +190,7 @@ void install(jsi::Runtime &rt,
|
|
|
190
190
|
tempDocPath = tempDocPath + "/" + args[1].asString(rt).utf8(rt);
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
BridgeResult result =
|
|
193
|
+
BridgeResult result = sqlite_remove(dbName, tempDocPath);
|
|
194
194
|
|
|
195
195
|
if (result.type == SQLiteError) {
|
|
196
196
|
throw std::runtime_error(result.message);
|
|
@@ -213,7 +213,7 @@ void install(jsi::Runtime &rt,
|
|
|
213
213
|
std::shared_ptr<std::vector<SmartHostObject>> metadata =
|
|
214
214
|
std::make_shared<std::vector<SmartHostObject>>();
|
|
215
215
|
|
|
216
|
-
auto status =
|
|
216
|
+
auto status = sqlite_execute(dbName, query, ¶ms, &results, metadata);
|
|
217
217
|
|
|
218
218
|
if (status.type == SQLiteError) {
|
|
219
219
|
throw std::runtime_error(status.message);
|
|
@@ -249,7 +249,7 @@ void install(jsi::Runtime &rt,
|
|
|
249
249
|
std::make_shared<std::vector<SmartHostObject>>();
|
|
250
250
|
|
|
251
251
|
auto status =
|
|
252
|
-
|
|
252
|
+
sqlite_execute(dbName, query, ¶ms, &results, metadata);
|
|
253
253
|
|
|
254
254
|
if (invalidated) {
|
|
255
255
|
return;
|
|
@@ -432,7 +432,7 @@ void install(jsi::Runtime &rt,
|
|
|
432
432
|
auto callback = std::make_shared<jsi::Value>(rt, args[1]);
|
|
433
433
|
|
|
434
434
|
if (callback->isUndefined() || callback->isNull()) {
|
|
435
|
-
|
|
435
|
+
sqlite_deregister_update_hook(dbName);
|
|
436
436
|
return {};
|
|
437
437
|
}
|
|
438
438
|
|
|
@@ -449,7 +449,7 @@ void install(jsi::Runtime &rt,
|
|
|
449
449
|
if (operation != "DELETE") {
|
|
450
450
|
std::string query = "SELECT * FROM " + tableName +
|
|
451
451
|
" where rowid = " + std::to_string(rowId) + ";";
|
|
452
|
-
|
|
452
|
+
sqlite_execute(dbName, query, ¶ms, &results, metadata);
|
|
453
453
|
}
|
|
454
454
|
|
|
455
455
|
invoker->invokeAsync(
|
|
@@ -474,7 +474,7 @@ void install(jsi::Runtime &rt,
|
|
|
474
474
|
});
|
|
475
475
|
};
|
|
476
476
|
|
|
477
|
-
|
|
477
|
+
sqlite_register_update_hook(dbName, std::move(hook));
|
|
478
478
|
|
|
479
479
|
return {};
|
|
480
480
|
});
|
|
@@ -490,7 +490,7 @@ void install(jsi::Runtime &rt,
|
|
|
490
490
|
auto dbName = args[0].asString(rt).utf8(rt);
|
|
491
491
|
auto callback = std::make_shared<jsi::Value>(rt, args[1]);
|
|
492
492
|
if (callback->isUndefined() || callback->isNull()) {
|
|
493
|
-
|
|
493
|
+
sqlite_deregister_commit_hook(dbName);
|
|
494
494
|
return {};
|
|
495
495
|
}
|
|
496
496
|
commitHooks[dbName] = callback;
|
|
@@ -500,7 +500,7 @@ void install(jsi::Runtime &rt,
|
|
|
500
500
|
[&rt, callback] { callback->asObject(rt).asFunction(rt).call(rt); });
|
|
501
501
|
};
|
|
502
502
|
|
|
503
|
-
|
|
503
|
+
sqlite_register_commit_hook(dbName, std::move(hook));
|
|
504
504
|
|
|
505
505
|
return {};
|
|
506
506
|
});
|
|
@@ -517,7 +517,7 @@ void install(jsi::Runtime &rt,
|
|
|
517
517
|
auto callback = std::make_shared<jsi::Value>(rt, args[1]);
|
|
518
518
|
|
|
519
519
|
if (callback->isUndefined() || callback->isNull()) {
|
|
520
|
-
|
|
520
|
+
sqlite_deregister_rollback_hook(dbName);
|
|
521
521
|
return {};
|
|
522
522
|
}
|
|
523
523
|
rollbackHooks[dbName] = callback;
|
|
@@ -527,7 +527,7 @@ void install(jsi::Runtime &rt,
|
|
|
527
527
|
[&rt, callback] { callback->asObject(rt).asFunction(rt).call(rt); });
|
|
528
528
|
};
|
|
529
529
|
|
|
530
|
-
|
|
530
|
+
sqlite_register_rollback_hook(dbName, std::move(hook));
|
|
531
531
|
return {};
|
|
532
532
|
});
|
|
533
533
|
|
package/cpp/bridge.cpp
CHANGED
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
#include "DumbHostObject.h"
|
|
3
3
|
#include "SmartHostObject.h"
|
|
4
4
|
#include "logs.h"
|
|
5
|
+
#include "utils.h"
|
|
5
6
|
#include <ctime>
|
|
6
|
-
#include <sstream>
|
|
7
|
-
#include <sys/stat.h>
|
|
8
|
-
#include <unistd.h>
|
|
9
7
|
#include <unordered_map>
|
|
10
8
|
#include <variant>
|
|
11
9
|
|
|
@@ -31,63 +29,16 @@ std::unordered_map<std::string, std::function<void(std::string dbName)>>
|
|
|
31
29
|
std::unordered_map<std::string,
|
|
32
30
|
std::function<void(std::string dbName)>>();
|
|
33
31
|
|
|
34
|
-
bool folder_exists(const std::string &foldername) {
|
|
35
|
-
struct stat buffer;
|
|
36
|
-
return (stat(foldername.c_str(), &buffer) == 0);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Portable wrapper for mkdir. Internally used by mkdir()
|
|
41
|
-
* @param[in] path the full path of the directory to create.
|
|
42
|
-
* @return zero on success, otherwise -1.
|
|
43
|
-
*/
|
|
44
|
-
int _mkdir(const char *path) {
|
|
45
|
-
#if _POSIX_C_SOURCE
|
|
46
|
-
return mkdir(path);
|
|
47
|
-
#else
|
|
48
|
-
return mkdir(path, 0755); // not sure if this works on mac
|
|
49
|
-
#endif
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Recursive, portable wrapper for mkdir.
|
|
54
|
-
* @param[in] path the full path of the directory to create.
|
|
55
|
-
* @return zero on success, otherwise -1.
|
|
56
|
-
*/
|
|
57
|
-
int mkdir(const char *path) {
|
|
58
|
-
std::string current_level = "/";
|
|
59
|
-
std::string level;
|
|
60
|
-
std::stringstream ss(path);
|
|
61
|
-
// First line is empty because it starts with /User
|
|
62
|
-
getline(ss, level, '/');
|
|
63
|
-
// split path using slash as a separator
|
|
64
|
-
while (getline(ss, level, '/')) {
|
|
65
|
-
current_level += level; // append folder to the current level
|
|
66
|
-
// create current level
|
|
67
|
-
if (!folder_exists(current_level) && _mkdir(current_level.c_str()) != 0)
|
|
68
|
-
return -1;
|
|
69
|
-
|
|
70
|
-
current_level += "/"; // don't forget to append a slash
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return 0;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
inline bool file_exists(const std::string &path) {
|
|
77
|
-
struct stat buffer;
|
|
78
|
-
return (stat(path.c_str(), &buffer) == 0);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
32
|
std::string get_db_path(std::string const dbName, std::string const lastPath) {
|
|
82
33
|
if (lastPath == ":memory:") {
|
|
83
34
|
return lastPath;
|
|
84
35
|
}
|
|
85
|
-
mkdir(lastPath
|
|
36
|
+
mkdir(lastPath);
|
|
86
37
|
return lastPath + "/" + dbName;
|
|
87
38
|
}
|
|
88
39
|
|
|
89
|
-
BridgeResult
|
|
90
|
-
|
|
40
|
+
BridgeResult sqlite_open(std::string const &dbName,
|
|
41
|
+
std::string const &lastPath) {
|
|
91
42
|
std::string dbPath = get_db_path(dbName, lastPath);
|
|
92
43
|
|
|
93
44
|
int sqlOpenFlags =
|
|
@@ -106,7 +57,7 @@ BridgeResult sqliteOpenDb(std::string const dbName,
|
|
|
106
57
|
return BridgeResult{.type = SQLiteOk, .affectedRows = 0};
|
|
107
58
|
}
|
|
108
59
|
|
|
109
|
-
BridgeResult
|
|
60
|
+
BridgeResult sqlite_close(std::string const &dbName) {
|
|
110
61
|
|
|
111
62
|
if (dbMap.count(dbName) == 0) {
|
|
112
63
|
return {
|
|
@@ -126,10 +77,10 @@ BridgeResult sqliteCloseDb(std::string const dbName) {
|
|
|
126
77
|
};
|
|
127
78
|
}
|
|
128
79
|
|
|
129
|
-
BridgeResult
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
80
|
+
BridgeResult sqlite_attach(std::string const &mainDBName,
|
|
81
|
+
std::string const &docPath,
|
|
82
|
+
std::string const &databaseToAttach,
|
|
83
|
+
std::string const &alias) {
|
|
133
84
|
/**
|
|
134
85
|
* There is no need to check if mainDBName is opened because
|
|
135
86
|
* sqliteExecuteLiteral will do that.
|
|
@@ -137,7 +88,7 @@ BridgeResult sqliteAttachDb(std::string const mainDBName,
|
|
|
137
88
|
std::string dbPath = get_db_path(databaseToAttach, docPath);
|
|
138
89
|
std::string statement = "ATTACH DATABASE '" + dbPath + "' AS " + alias;
|
|
139
90
|
|
|
140
|
-
BridgeResult result =
|
|
91
|
+
BridgeResult result = sqlite_execute_literal(mainDBName, statement);
|
|
141
92
|
|
|
142
93
|
if (result.type == SQLiteError) {
|
|
143
94
|
return {
|
|
@@ -151,14 +102,14 @@ BridgeResult sqliteAttachDb(std::string const mainDBName,
|
|
|
151
102
|
};
|
|
152
103
|
}
|
|
153
104
|
|
|
154
|
-
BridgeResult
|
|
155
|
-
|
|
105
|
+
BridgeResult sqlite_detach(std::string const &mainDBName,
|
|
106
|
+
std::string const &alias) {
|
|
156
107
|
/**
|
|
157
108
|
* There is no need to check if mainDBName is opened because
|
|
158
109
|
* sqliteExecuteLiteral will do that.
|
|
159
110
|
* */
|
|
160
111
|
std::string statement = "DETACH DATABASE " + alias;
|
|
161
|
-
BridgeResult result =
|
|
112
|
+
BridgeResult result = sqlite_execute_literal(mainDBName, statement);
|
|
162
113
|
if (result.type == SQLiteError) {
|
|
163
114
|
return BridgeResult{
|
|
164
115
|
.type = SQLiteError,
|
|
@@ -171,10 +122,10 @@ BridgeResult sqliteDetachDb(std::string const mainDBName,
|
|
|
171
122
|
};
|
|
172
123
|
}
|
|
173
124
|
|
|
174
|
-
BridgeResult
|
|
175
|
-
|
|
125
|
+
BridgeResult sqlite_remove(std::string const &dbName,
|
|
126
|
+
std::string const &docPath) {
|
|
176
127
|
if (dbMap.count(dbName) == 1) {
|
|
177
|
-
BridgeResult closeResult =
|
|
128
|
+
BridgeResult closeResult = sqlite_close(dbName);
|
|
178
129
|
if (closeResult.type == SQLiteError) {
|
|
179
130
|
return closeResult;
|
|
180
131
|
}
|
|
@@ -230,7 +181,7 @@ void sqlite_bind_statement(sqlite3_stmt *statement,
|
|
|
230
181
|
}
|
|
231
182
|
|
|
232
183
|
BridgeResult sqlite_execute_prepared_statement(
|
|
233
|
-
std::string const dbName, sqlite3_stmt *statement,
|
|
184
|
+
std::string const &dbName, sqlite3_stmt *statement,
|
|
234
185
|
std::vector<DumbHostObject> *results,
|
|
235
186
|
std::shared_ptr<std::vector<SmartHostObject>> metadatas) {
|
|
236
187
|
if (dbMap.find(dbName) == dbMap.end()) {
|
|
@@ -368,7 +319,7 @@ BridgeResult sqlite_execute_prepared_statement(
|
|
|
368
319
|
.insertId = static_cast<double>(latestInsertRowId)};
|
|
369
320
|
}
|
|
370
321
|
|
|
371
|
-
sqlite3_stmt *sqlite_prepare_statement(std::string const dbName,
|
|
322
|
+
sqlite3_stmt *sqlite_prepare_statement(std::string const &dbName,
|
|
372
323
|
std::string const &query) {
|
|
373
324
|
if (dbMap.find(dbName) == dbMap.end()) {
|
|
374
325
|
throw std::runtime_error("Database not opened");
|
|
@@ -392,10 +343,10 @@ sqlite3_stmt *sqlite_prepare_statement(std::string const dbName,
|
|
|
392
343
|
}
|
|
393
344
|
|
|
394
345
|
BridgeResult
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
346
|
+
sqlite_execute(std::string const &dbName, std::string const &query,
|
|
347
|
+
const std::vector<JSVariant> *params,
|
|
348
|
+
std::vector<DumbHostObject> *results,
|
|
349
|
+
std::shared_ptr<std::vector<SmartHostObject>> metadatas) {
|
|
399
350
|
|
|
400
351
|
if (dbMap.find(dbName) == dbMap.end()) {
|
|
401
352
|
return {.type = SQLiteError,
|
|
@@ -427,7 +378,7 @@ sqliteExecute(std::string const dbName, std::string const &query,
|
|
|
427
378
|
.message = "[op-sqlite] SQL statement error:" +
|
|
428
379
|
std::to_string(statementStatus) +
|
|
429
380
|
" description:" + std::string(message) +
|
|
430
|
-
"
|
|
381
|
+
". See error codes: https://www.sqlite.org/rescode.html",
|
|
431
382
|
};
|
|
432
383
|
}
|
|
433
384
|
|
|
@@ -567,8 +518,8 @@ sqliteExecute(std::string const dbName, std::string const &query,
|
|
|
567
518
|
.insertId = static_cast<double>(latestInsertRowId)};
|
|
568
519
|
}
|
|
569
520
|
|
|
570
|
-
BridgeResult
|
|
571
|
-
|
|
521
|
+
BridgeResult sqlite_execute_literal(std::string const &dbName,
|
|
522
|
+
std::string const &query) {
|
|
572
523
|
if (dbMap.count(dbName) == 0) {
|
|
573
524
|
return {SQLiteError, "[op-sqlite] Database not opened: " + dbName};
|
|
574
525
|
}
|
|
@@ -621,7 +572,7 @@ BridgeResult sqliteExecuteLiteral(std::string const dbName,
|
|
|
621
572
|
return {SQLiteOk, "", changedRowCount};
|
|
622
573
|
}
|
|
623
574
|
|
|
624
|
-
void
|
|
575
|
+
void sqlite_close_all() {
|
|
625
576
|
for (auto const &x : dbMap) {
|
|
626
577
|
// Interrupt will make all pending operations to fail with SQLITE_INTERRUPT
|
|
627
578
|
// The ongoing work from threads will then fail ASAP
|
|
@@ -632,7 +583,7 @@ void sqliteCloseAll() {
|
|
|
632
583
|
dbMap.clear();
|
|
633
584
|
}
|
|
634
585
|
|
|
635
|
-
std::string
|
|
586
|
+
std::string operation_to_string(int operation_type) {
|
|
636
587
|
switch (operation_type) {
|
|
637
588
|
case SQLITE_INSERT:
|
|
638
589
|
return "INSERT";
|
|
@@ -652,12 +603,12 @@ void update_callback(void *dbName, int operation_type, char const *database,
|
|
|
652
603
|
char const *table, sqlite3_int64 rowid) {
|
|
653
604
|
std::string &strDbName = *(static_cast<std::string *>(dbName));
|
|
654
605
|
auto callback = updateCallbackMap[strDbName];
|
|
655
|
-
callback(strDbName, std::string(table),
|
|
606
|
+
callback(strDbName, std::string(table), operation_to_string(operation_type),
|
|
656
607
|
static_cast<int>(rowid));
|
|
657
608
|
}
|
|
658
609
|
|
|
659
|
-
BridgeResult
|
|
660
|
-
std::string const dbName,
|
|
610
|
+
BridgeResult sqlite_register_update_hook(
|
|
611
|
+
std::string const &dbName,
|
|
661
612
|
std::function<void(std::string dbName, std::string tableName,
|
|
662
613
|
std::string operation, int rowId)> const callback) {
|
|
663
614
|
if (dbMap.count(dbName) == 0) {
|
|
@@ -680,7 +631,7 @@ BridgeResult registerUpdateHook(
|
|
|
680
631
|
return {SQLiteOk};
|
|
681
632
|
}
|
|
682
633
|
|
|
683
|
-
BridgeResult
|
|
634
|
+
BridgeResult sqlite_deregister_update_hook(std::string const &dbName) {
|
|
684
635
|
if (dbMap.count(dbName) == 0) {
|
|
685
636
|
return {SQLiteError, "[op-sqlite] Database not opened: " + dbName};
|
|
686
637
|
}
|
|
@@ -701,9 +652,9 @@ int commit_callback(void *dbName) {
|
|
|
701
652
|
return 0;
|
|
702
653
|
}
|
|
703
654
|
|
|
704
|
-
BridgeResult
|
|
705
|
-
|
|
706
|
-
|
|
655
|
+
BridgeResult sqlite_register_commit_hook(
|
|
656
|
+
std::string const &dbName,
|
|
657
|
+
std::function<void(std::string dbName)> const callback) {
|
|
707
658
|
if (dbMap.count(dbName) == 0) {
|
|
708
659
|
return {SQLiteError, "[op-sqlite] Database not opened: " + dbName};
|
|
709
660
|
}
|
|
@@ -724,7 +675,7 @@ registerCommitHook(std::string const dbName,
|
|
|
724
675
|
return {SQLiteOk};
|
|
725
676
|
}
|
|
726
677
|
|
|
727
|
-
BridgeResult
|
|
678
|
+
BridgeResult sqlite_deregister_commit_hook(std::string const &dbName) {
|
|
728
679
|
if (dbMap.count(dbName) == 0) {
|
|
729
680
|
return {SQLiteError, "[op-sqlite] Database not opened: " + dbName};
|
|
730
681
|
}
|
|
@@ -742,9 +693,9 @@ void rollback_callback(void *dbName) {
|
|
|
742
693
|
callback(strDbName);
|
|
743
694
|
}
|
|
744
695
|
|
|
745
|
-
BridgeResult
|
|
746
|
-
|
|
747
|
-
|
|
696
|
+
BridgeResult sqlite_register_rollback_hook(
|
|
697
|
+
std::string const &dbName,
|
|
698
|
+
std::function<void(std::string dbName)> const callback) {
|
|
748
699
|
if (dbMap.count(dbName) == 0) {
|
|
749
700
|
return {SQLiteError, "[op-sqlite] Database not opened: " + dbName};
|
|
750
701
|
}
|
|
@@ -765,7 +716,7 @@ registerRollbackHook(std::string const dbName,
|
|
|
765
716
|
return {SQLiteOk};
|
|
766
717
|
}
|
|
767
718
|
|
|
768
|
-
BridgeResult
|
|
719
|
+
BridgeResult sqlite_deregister_rollback_hook(std::string const &dbName) {
|
|
769
720
|
if (dbMap.count(dbName) == 0) {
|
|
770
721
|
return {SQLiteError, "[op-sqlite] Database not opened: " + dbName};
|
|
771
722
|
}
|
package/cpp/bridge.h
CHANGED
|
@@ -12,54 +12,54 @@ namespace opsqlite {
|
|
|
12
12
|
|
|
13
13
|
namespace jsi = facebook::jsi;
|
|
14
14
|
|
|
15
|
-
BridgeResult
|
|
15
|
+
BridgeResult sqlite_open(std::string const &dbName, std::string const &dbPath);
|
|
16
16
|
|
|
17
|
-
BridgeResult
|
|
17
|
+
BridgeResult sqlite_close(std::string const &dbName);
|
|
18
18
|
|
|
19
|
-
BridgeResult
|
|
20
|
-
|
|
19
|
+
BridgeResult sqlite_remove(std::string const &dbName,
|
|
20
|
+
std::string const &docPath);
|
|
21
21
|
|
|
22
|
-
BridgeResult
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
BridgeResult sqlite_attach(std::string const &mainDBName,
|
|
23
|
+
std::string const &docPath,
|
|
24
|
+
std::string const &databaseToAttach,
|
|
25
|
+
std::string const &alias);
|
|
26
26
|
|
|
27
|
-
BridgeResult
|
|
28
|
-
|
|
27
|
+
BridgeResult sqlite_detach(std::string const &mainDBName,
|
|
28
|
+
std::string const &alias);
|
|
29
29
|
|
|
30
30
|
BridgeResult
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
sqlite_execute(std::string const &dbName, std::string const &query,
|
|
32
|
+
const std::vector<JSVariant> *params,
|
|
33
|
+
std::vector<DumbHostObject> *results,
|
|
34
|
+
std::shared_ptr<std::vector<SmartHostObject>> metadatas);
|
|
35
35
|
|
|
36
|
-
BridgeResult
|
|
37
|
-
|
|
36
|
+
BridgeResult sqlite_execute_literal(std::string const &dbName,
|
|
37
|
+
std::string const &query);
|
|
38
38
|
|
|
39
|
-
void
|
|
39
|
+
void sqlite_close_all();
|
|
40
40
|
|
|
41
|
-
BridgeResult
|
|
42
|
-
std::string const dbName,
|
|
41
|
+
BridgeResult sqlite_register_update_hook(
|
|
42
|
+
std::string const &dbName,
|
|
43
43
|
std::function<void(std::string dbName, std::string tableName,
|
|
44
44
|
std::string operation, int rowId)> const callback);
|
|
45
|
-
BridgeResult
|
|
46
|
-
BridgeResult
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
BridgeResult
|
|
50
|
-
BridgeResult
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
BridgeResult
|
|
54
|
-
|
|
55
|
-
sqlite3_stmt *sqlite_prepare_statement(std::string const dbName,
|
|
45
|
+
BridgeResult sqlite_deregister_update_hook(std::string const &dbName);
|
|
46
|
+
BridgeResult sqlite_register_commit_hook(
|
|
47
|
+
std::string const &dbName,
|
|
48
|
+
std::function<void(std::string dbName)> const callback);
|
|
49
|
+
BridgeResult sqlite_deregister_commit_hook(std::string const &dbName);
|
|
50
|
+
BridgeResult sqlite_register_rollback_hook(
|
|
51
|
+
std::string const &dbName,
|
|
52
|
+
std::function<void(std::string dbName)> const callback);
|
|
53
|
+
BridgeResult sqlite_deregister_rollback_hook(std::string const &dbName);
|
|
54
|
+
|
|
55
|
+
sqlite3_stmt *sqlite_prepare_statement(std::string const &dbName,
|
|
56
56
|
std::string const &query);
|
|
57
57
|
|
|
58
58
|
void sqlite_bind_statement(sqlite3_stmt *statement,
|
|
59
59
|
const std::vector<JSVariant> *params);
|
|
60
60
|
|
|
61
61
|
BridgeResult sqlite_execute_prepared_statement(
|
|
62
|
-
std::string const dbName, sqlite3_stmt *statement,
|
|
62
|
+
std::string const &dbName, sqlite3_stmt *statement,
|
|
63
63
|
std::vector<DumbHostObject> *results,
|
|
64
64
|
std::shared_ptr<std::vector<SmartHostObject>> metadatas);
|
|
65
65
|
} // namespace opsqlite
|
package/cpp/sqlbatchexecutor.cpp
CHANGED
|
@@ -57,15 +57,15 @@ BatchResult sqliteExecuteBatch(std::string dbName,
|
|
|
57
57
|
|
|
58
58
|
try {
|
|
59
59
|
int affectedRows = 0;
|
|
60
|
-
|
|
60
|
+
sqlite_execute_literal(dbName, "BEGIN EXCLUSIVE TRANSACTION");
|
|
61
61
|
for (int i = 0; i < commandCount; i++) {
|
|
62
62
|
auto command = commands->at(i);
|
|
63
63
|
// We do not provide a datastructure to receive query data because we
|
|
64
64
|
// don't need/want to handle this results in a batch execution
|
|
65
|
-
auto result =
|
|
66
|
-
|
|
65
|
+
auto result = sqlite_execute(dbName, command.sql, command.params.get(),
|
|
66
|
+
nullptr, nullptr);
|
|
67
67
|
if (result.type == SQLiteError) {
|
|
68
|
-
|
|
68
|
+
sqlite_execute_literal(dbName, "ROLLBACK");
|
|
69
69
|
return BatchResult{
|
|
70
70
|
.type = SQLiteError,
|
|
71
71
|
.message = result.message,
|
|
@@ -74,14 +74,14 @@ BatchResult sqliteExecuteBatch(std::string dbName,
|
|
|
74
74
|
affectedRows += result.affectedRows;
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
|
-
|
|
77
|
+
sqlite_execute_literal(dbName, "COMMIT");
|
|
78
78
|
return BatchResult{
|
|
79
79
|
.type = SQLiteOk,
|
|
80
80
|
.affectedRows = affectedRows,
|
|
81
81
|
.commands = static_cast<int>(commandCount),
|
|
82
82
|
};
|
|
83
83
|
} catch (std::exception &exc) {
|
|
84
|
-
|
|
84
|
+
sqlite_execute_literal(dbName, "ROLLBACK");
|
|
85
85
|
return BatchResult{
|
|
86
86
|
.type = SQLiteError,
|
|
87
87
|
.message = exc.what(),
|
package/cpp/utils.cpp
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
#include "bridge.h"
|
|
4
4
|
#include <fstream>
|
|
5
5
|
#include <iostream>
|
|
6
|
+
#include <sstream>
|
|
7
|
+
#include <sys/stat.h>
|
|
8
|
+
#include <unistd.h>
|
|
6
9
|
|
|
7
10
|
namespace opsqlite {
|
|
8
11
|
|
|
@@ -38,6 +41,49 @@ jsi::Value toJSI(jsi::Runtime &rt, JSVariant value) {
|
|
|
38
41
|
return jsi::Value::null();
|
|
39
42
|
}
|
|
40
43
|
|
|
44
|
+
JSVariant toVariant(jsi::Runtime &rt, const jsi::Value &value) {
|
|
45
|
+
if (value.isNull() || value.isUndefined()) {
|
|
46
|
+
return JSVariant(nullptr);
|
|
47
|
+
} else if (value.isBool()) {
|
|
48
|
+
return JSVariant(value.getBool());
|
|
49
|
+
} else if (value.isNumber()) {
|
|
50
|
+
double doubleVal = value.asNumber();
|
|
51
|
+
int intVal = (int)doubleVal;
|
|
52
|
+
long long longVal = (long)doubleVal;
|
|
53
|
+
if (intVal == doubleVal) {
|
|
54
|
+
return JSVariant(intVal);
|
|
55
|
+
} else if (longVal == doubleVal) {
|
|
56
|
+
return JSVariant(longVal);
|
|
57
|
+
} else {
|
|
58
|
+
return JSVariant(doubleVal);
|
|
59
|
+
}
|
|
60
|
+
} else if (value.isString()) {
|
|
61
|
+
std::string strVal = value.asString(rt).utf8(rt);
|
|
62
|
+
return JSVariant(strVal);
|
|
63
|
+
} else if (value.isObject()) {
|
|
64
|
+
auto obj = value.asObject(rt);
|
|
65
|
+
if (obj.isArrayBuffer(rt)) {
|
|
66
|
+
auto buffer = obj.getArrayBuffer(rt);
|
|
67
|
+
|
|
68
|
+
uint8_t *data = new uint8_t[buffer.size(rt)];
|
|
69
|
+
// You cannot share raw memory between native and JS
|
|
70
|
+
// always copy the data
|
|
71
|
+
// see https://github.com/facebook/hermes/pull/419 and
|
|
72
|
+
// https://github.com/facebook/hermes/issues/564.
|
|
73
|
+
memcpy(data, buffer.data(rt), buffer.size(rt));
|
|
74
|
+
|
|
75
|
+
return JSVariant(ArrayBuffer{.data = std::shared_ptr<uint8_t>{data},
|
|
76
|
+
.size = buffer.size(rt)});
|
|
77
|
+
} else {
|
|
78
|
+
throw std::invalid_argument(
|
|
79
|
+
"Unknown JSI ArrayBuffer to variant value conversion, received "
|
|
80
|
+
"object instead of ArrayBuffer");
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
throw std::invalid_argument("Unknown JSI to variant value conversion");
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
41
87
|
std::vector<JSVariant> toVariantVec(jsi::Runtime &rt,
|
|
42
88
|
jsi::Value const ¶ms) {
|
|
43
89
|
std::vector<JSVariant> res;
|
|
@@ -148,12 +194,12 @@ BatchResult importSQLFile(std::string dbName, std::string fileLocation) {
|
|
|
148
194
|
try {
|
|
149
195
|
int affectedRows = 0;
|
|
150
196
|
int commands = 0;
|
|
151
|
-
|
|
197
|
+
sqlite_execute_literal(dbName, "BEGIN EXCLUSIVE TRANSACTION");
|
|
152
198
|
while (std::getline(sqFile, line, '\n')) {
|
|
153
199
|
if (!line.empty()) {
|
|
154
|
-
BridgeResult result =
|
|
200
|
+
BridgeResult result = sqlite_execute_literal(dbName, line);
|
|
155
201
|
if (result.type == SQLiteError) {
|
|
156
|
-
|
|
202
|
+
sqlite_execute_literal(dbName, "ROLLBACK");
|
|
157
203
|
sqFile.close();
|
|
158
204
|
return {SQLiteError, result.message, 0, commands};
|
|
159
205
|
} else {
|
|
@@ -163,11 +209,11 @@ BatchResult importSQLFile(std::string dbName, std::string fileLocation) {
|
|
|
163
209
|
}
|
|
164
210
|
}
|
|
165
211
|
sqFile.close();
|
|
166
|
-
|
|
212
|
+
sqlite_execute_literal(dbName, "COMMIT");
|
|
167
213
|
return {SQLiteOk, "", affectedRows, commands};
|
|
168
214
|
} catch (...) {
|
|
169
215
|
sqFile.close();
|
|
170
|
-
|
|
216
|
+
sqlite_execute_literal(dbName, "ROLLBACK");
|
|
171
217
|
return {SQLiteError,
|
|
172
218
|
"[op-sqlite][loadSQLFile] Unexpected error, transaction was "
|
|
173
219
|
"rolledback",
|
|
@@ -178,4 +224,19 @@ BatchResult importSQLFile(std::string dbName, std::string fileLocation) {
|
|
|
178
224
|
}
|
|
179
225
|
}
|
|
180
226
|
|
|
227
|
+
bool folder_exists(const std::string &foldername) {
|
|
228
|
+
struct stat buffer;
|
|
229
|
+
return (stat(foldername.c_str(), &buffer) == 0);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
bool file_exists(const std::string &path) {
|
|
233
|
+
struct stat buffer;
|
|
234
|
+
return (stat(path.c_str(), &buffer) == 0);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
int mkdir(std::string const &path) {
|
|
238
|
+
std::filesystem::create_directories(path);
|
|
239
|
+
return 0;
|
|
240
|
+
}
|
|
241
|
+
|
|
181
242
|
} // namespace opsqlite
|
package/cpp/utils.h
CHANGED
|
@@ -33,6 +33,8 @@ struct BatchResult {
|
|
|
33
33
|
|
|
34
34
|
jsi::Value toJSI(jsi::Runtime &rt, JSVariant value);
|
|
35
35
|
|
|
36
|
+
JSVariant toVariant(jsi::Runtime &rt, jsi::Value const &value);
|
|
37
|
+
|
|
36
38
|
std::vector<JSVariant> toVariantVec(jsi::Runtime &rt, jsi::Value const &args);
|
|
37
39
|
|
|
38
40
|
jsi::Value createResult(jsi::Runtime &rt, BridgeResult status,
|
|
@@ -41,6 +43,12 @@ jsi::Value createResult(jsi::Runtime &rt, BridgeResult status,
|
|
|
41
43
|
|
|
42
44
|
BatchResult importSQLFile(std::string dbName, std::string fileLocation);
|
|
43
45
|
|
|
46
|
+
int mkdir(const std::string &path);
|
|
47
|
+
|
|
48
|
+
bool folder_exists(const std::string &foldername);
|
|
49
|
+
|
|
50
|
+
bool file_exists(const std::string &path);
|
|
51
|
+
|
|
44
52
|
} // namespace opsqlite
|
|
45
53
|
|
|
46
54
|
#endif /* utils_h */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@op-engineering/op-sqlite",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.9",
|
|
4
4
|
"description": "Next generation SQLite for React Native",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/module/index",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"typescript": "tsc --noEmit",
|
|
27
27
|
"prepare": "bob build",
|
|
28
28
|
"example": "yarn --cwd example",
|
|
29
|
-
"pods": "cd example &&
|
|
29
|
+
"pods": "cd example && yarn pods",
|
|
30
30
|
"bootstrap": "yarn example && yarn && yarn pods",
|
|
31
31
|
"bump": "./bump-version.sh"
|
|
32
32
|
},
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"lefthook": "^1.5.5",
|
|
50
50
|
"react": "18.2.0",
|
|
51
|
-
"react-native": "0.73.
|
|
51
|
+
"react-native": "0.73.1",
|
|
52
52
|
"react-native-builder-bob": "^0.23.2",
|
|
53
53
|
"typescript": "5.0.4"
|
|
54
54
|
},
|