@op-engineering/op-sqlite 2.0.1 → 2.0.2
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 +53 -17
- package/android/CMakeLists.txt +4 -2
- package/cpp/DumbHostObject.cpp +32 -27
- package/cpp/DumbHostObject.h +16 -16
- package/cpp/PreparedStatementHostObject.cpp +88 -0
- package/cpp/PreparedStatementHostObject.h +36 -0
- package/cpp/SmartHostObject.cpp +33 -0
- package/cpp/SmartHostObject.h +26 -0
- package/cpp/ThreadPool.cpp +103 -116
- package/cpp/ThreadPool.h +27 -27
- package/cpp/bindings.cpp +525 -530
- package/cpp/bindings.h +6 -5
- package/cpp/bridge.cpp +713 -651
- package/cpp/bridge.h +41 -19
- package/cpp/logs.h +17 -17
- package/cpp/macros.h +1 -1
- package/cpp/sqlbatchexecutor.cpp +75 -77
- package/cpp/sqlbatchexecutor.h +9 -8
- package/cpp/sqlite3.h +1295 -1389
- package/cpp/types.h +5 -4
- package/cpp/utils.cpp +159 -191
- package/cpp/utils.h +28 -35
- package/lib/commonjs/index.js +2 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/index.js +2 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/index.d.ts +6 -0
- package/package.json +2 -1
- package/src/index.ts +8 -0
- package/cpp/DynamicHostObject.cpp +0 -32
- package/cpp/DynamicHostObject.h +0 -26
package/cpp/bridge.cpp
CHANGED
|
@@ -1,706 +1,768 @@
|
|
|
1
1
|
#include "bridge.h"
|
|
2
|
-
#include
|
|
3
|
-
#include
|
|
4
|
-
#include
|
|
2
|
+
#include "DumbHostObject.h"
|
|
3
|
+
#include "SmartHostObject.h"
|
|
4
|
+
#include "logs.h"
|
|
5
5
|
#include <ctime>
|
|
6
|
-
#include <
|
|
6
|
+
#include <sstream>
|
|
7
7
|
#include <sys/stat.h>
|
|
8
|
+
#include <unistd.h>
|
|
8
9
|
#include <unordered_map>
|
|
9
|
-
#include "logs.h"
|
|
10
|
-
#include "DynamicHostObject.h"
|
|
11
|
-
#include "DumbHostObject.h"
|
|
12
10
|
#include <variant>
|
|
13
11
|
|
|
14
12
|
namespace opsqlite {
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
std::unordered_map<
|
|
14
|
+
std::unordered_map<std::string, sqlite3 *> dbMap =
|
|
15
|
+
std::unordered_map<std::string, sqlite3 *>();
|
|
16
|
+
std::unordered_map<std::string,
|
|
17
|
+
std::function<void(std::string dbName, std::string tableName,
|
|
18
|
+
std::string operation, int rowId)>>
|
|
19
|
+
updateCallbackMap = std::unordered_map<
|
|
18
20
|
std::string,
|
|
19
|
-
std::function<void
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
std::function<void(std::string dbName, std::string tableName,
|
|
22
|
+
std::string operation, int rowId)>>();
|
|
23
|
+
|
|
24
|
+
std::unordered_map<std::string, std::function<void(std::string dbName)>>
|
|
25
|
+
commitCallbackMap =
|
|
26
|
+
std::unordered_map<std::string,
|
|
27
|
+
std::function<void(std::string dbName)>>();
|
|
28
|
+
|
|
29
|
+
std::unordered_map<std::string, std::function<void(std::string dbName)>>
|
|
30
|
+
rollbackCallbackMap =
|
|
31
|
+
std::unordered_map<std::string,
|
|
32
|
+
std::function<void(std::string dbName)>>();
|
|
33
|
+
|
|
34
|
+
bool folder_exists(const std::string &foldername) {
|
|
35
|
+
struct stat buffer;
|
|
36
|
+
return (stat(foldername.c_str(), &buffer) == 0);
|
|
37
|
+
}
|
|
28
38
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
+
}
|
|
33
51
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
+
}
|
|
39
75
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
*/
|
|
45
|
-
int _mkdir(const char *path)
|
|
46
|
-
{
|
|
47
|
-
#if _POSIX_C_SOURCE
|
|
48
|
-
return mkdir(path);
|
|
49
|
-
#else
|
|
50
|
-
return mkdir(path, 0755); // not sure if this works on mac
|
|
51
|
-
#endif
|
|
52
|
-
}
|
|
76
|
+
inline bool file_exists(const std::string &path) {
|
|
77
|
+
struct stat buffer;
|
|
78
|
+
return (stat(path.c_str(), &buffer) == 0);
|
|
79
|
+
}
|
|
53
80
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
std::string current_level = "/";
|
|
62
|
-
std::string level;
|
|
63
|
-
std::stringstream ss(path);
|
|
64
|
-
// First line is empty because it starts with /User
|
|
65
|
-
getline(ss, level, '/');
|
|
66
|
-
// split path using slash as a separator
|
|
67
|
-
while (getline(ss, level, '/'))
|
|
68
|
-
{
|
|
69
|
-
current_level += level; // append folder to the current level
|
|
70
|
-
// create current level
|
|
71
|
-
if (!folder_exists(current_level) && _mkdir(current_level.c_str()) != 0)
|
|
72
|
-
return -1;
|
|
73
|
-
|
|
74
|
-
current_level += "/"; // don't forget to append a slash
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return 0;
|
|
78
|
-
}
|
|
81
|
+
std::string get_db_path(std::string const dbName, std::string const lastPath) {
|
|
82
|
+
if (lastPath == ":memory:") {
|
|
83
|
+
return lastPath;
|
|
84
|
+
}
|
|
85
|
+
mkdir(lastPath.c_str());
|
|
86
|
+
return lastPath + "/" + dbName;
|
|
87
|
+
}
|
|
79
88
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
return (stat(path.c_str(), &buffer) == 0);
|
|
84
|
-
}
|
|
89
|
+
BridgeResult sqliteOpenDb(std::string const dbName,
|
|
90
|
+
std::string const lastPath) {
|
|
91
|
+
std::string dbPath = get_db_path(dbName, lastPath);
|
|
85
92
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if(lastPath == ":memory:") {
|
|
89
|
-
return lastPath;
|
|
90
|
-
}
|
|
91
|
-
mkdir(lastPath.c_str());
|
|
92
|
-
return lastPath + "/" + dbName;
|
|
93
|
-
}
|
|
93
|
+
int sqlOpenFlags =
|
|
94
|
+
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX;
|
|
94
95
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
96
|
+
sqlite3 *db;
|
|
97
|
+
int exit = 0;
|
|
98
|
+
exit = sqlite3_open_v2(dbPath.c_str(), &db, sqlOpenFlags, nullptr);
|
|
99
|
+
|
|
100
|
+
if (exit != SQLITE_OK) {
|
|
101
|
+
return {.type = SQLiteError, .message = sqlite3_errmsg(db)};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
dbMap[dbName] = db;
|
|
105
|
+
|
|
106
|
+
return BridgeResult{.type = SQLiteOk, .affectedRows = 0};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
BridgeResult sqliteCloseDb(std::string const dbName) {
|
|
110
|
+
|
|
111
|
+
if (dbMap.count(dbName) == 0) {
|
|
112
|
+
return {
|
|
113
|
+
.type = SQLiteError,
|
|
114
|
+
.message = dbName + " is not open",
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
sqlite3 *db = dbMap[dbName];
|
|
112
119
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
120
|
+
sqlite3_close_v2(db);
|
|
121
|
+
|
|
122
|
+
dbMap.erase(dbName);
|
|
123
|
+
|
|
124
|
+
return BridgeResult{
|
|
125
|
+
.type = SQLiteOk,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
BridgeResult sqliteAttachDb(std::string const mainDBName,
|
|
130
|
+
std::string const docPath,
|
|
131
|
+
std::string const databaseToAttach,
|
|
132
|
+
std::string const alias) {
|
|
133
|
+
/**
|
|
134
|
+
* There is no need to check if mainDBName is opened because
|
|
135
|
+
* sqliteExecuteLiteral will do that.
|
|
136
|
+
* */
|
|
137
|
+
std::string dbPath = get_db_path(databaseToAttach, docPath);
|
|
138
|
+
std::string statement = "ATTACH DATABASE '" + dbPath + "' AS " + alias;
|
|
139
|
+
|
|
140
|
+
BridgeResult result = sqliteExecuteLiteral(mainDBName, statement);
|
|
141
|
+
|
|
142
|
+
if (result.type == SQLiteError) {
|
|
143
|
+
return {
|
|
144
|
+
.type = SQLiteError,
|
|
145
|
+
.message = mainDBName + " was unable to attach another database: " +
|
|
146
|
+
std::string(result.message),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
.type = SQLiteOk,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
BridgeResult sqliteDetachDb(std::string const mainDBName,
|
|
155
|
+
std::string const alias) {
|
|
156
|
+
/**
|
|
157
|
+
* There is no need to check if mainDBName is opened because
|
|
158
|
+
* sqliteExecuteLiteral will do that.
|
|
159
|
+
* */
|
|
160
|
+
std::string statement = "DETACH DATABASE " + alias;
|
|
161
|
+
BridgeResult result = sqliteExecuteLiteral(mainDBName, statement);
|
|
162
|
+
if (result.type == SQLiteError) {
|
|
163
|
+
return BridgeResult{
|
|
164
|
+
.type = SQLiteError,
|
|
165
|
+
.message = mainDBName + "was unable to detach database: " +
|
|
166
|
+
std::string(result.message),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
return BridgeResult{
|
|
170
|
+
.type = SQLiteOk,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
BridgeResult sqliteRemoveDb(std::string const dbName,
|
|
175
|
+
std::string const docPath) {
|
|
176
|
+
if (dbMap.count(dbName) == 1) {
|
|
177
|
+
BridgeResult closeResult = sqliteCloseDb(dbName);
|
|
178
|
+
if (closeResult.type == SQLiteError) {
|
|
179
|
+
return closeResult;
|
|
119
180
|
}
|
|
181
|
+
}
|
|
120
182
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
183
|
+
std::string dbPath = get_db_path(dbName, docPath);
|
|
184
|
+
|
|
185
|
+
if (!file_exists(dbPath)) {
|
|
186
|
+
return {.type = SQLiteError,
|
|
187
|
+
.message = "[op-sqlite]: Database file not found" + dbPath};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
remove(dbPath.c_str());
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
.type = SQLiteOk,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
inline void bindStatement(sqlite3_stmt *statement,
|
|
198
|
+
const std::vector<JSVariant> *values) {
|
|
199
|
+
size_t size = values->size();
|
|
200
|
+
|
|
201
|
+
for (int ii = 0; ii < size; ii++) {
|
|
202
|
+
int sqIndex = ii + 1;
|
|
203
|
+
JSVariant value = values->at(ii);
|
|
204
|
+
|
|
205
|
+
if (std::holds_alternative<bool>(value)) {
|
|
206
|
+
sqlite3_bind_int(statement, sqIndex, std::get<bool>(value));
|
|
207
|
+
} else if (std::holds_alternative<int>(value)) {
|
|
208
|
+
sqlite3_bind_int(statement, sqIndex, std::get<int>(value));
|
|
209
|
+
} else if (std::holds_alternative<long long>(value)) {
|
|
210
|
+
sqlite3_bind_double(statement, sqIndex, std::get<long long>(value));
|
|
211
|
+
} else if (std::holds_alternative<double>(value)) {
|
|
212
|
+
sqlite3_bind_double(statement, sqIndex, std::get<double>(value));
|
|
213
|
+
} else if (std::holds_alternative<std::string>(value)) {
|
|
214
|
+
std::string str = std::get<std::string>(value);
|
|
215
|
+
sqlite3_bind_text(statement, sqIndex, str.c_str(), str.length(),
|
|
216
|
+
SQLITE_TRANSIENT);
|
|
217
|
+
} else if (std::holds_alternative<ArrayBuffer>(value)) {
|
|
218
|
+
ArrayBuffer buffer = std::get<ArrayBuffer>(value);
|
|
219
|
+
sqlite3_bind_blob(statement, sqIndex, buffer.data.get(), buffer.size,
|
|
220
|
+
SQLITE_STATIC);
|
|
221
|
+
} else {
|
|
222
|
+
sqlite3_bind_null(statement, sqIndex);
|
|
141
223
|
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
void sqlite_bind_statement(sqlite3_stmt *statement,
|
|
228
|
+
const std::vector<JSVariant> *params) {
|
|
229
|
+
bindStatement(statement, params);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
BridgeResult sqlite_execute_prepared_statement(
|
|
233
|
+
std::string const dbName, sqlite3_stmt *statement,
|
|
234
|
+
std::vector<DumbHostObject> *results,
|
|
235
|
+
std::shared_ptr<std::vector<SmartHostObject>> metadatas) {
|
|
236
|
+
if (dbMap.find(dbName) == dbMap.end()) {
|
|
237
|
+
return {.type = SQLiteError,
|
|
238
|
+
.message = "[op-sqlite]: Database " + dbName + " is not open"};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
sqlite3_reset(statement);
|
|
242
|
+
|
|
243
|
+
sqlite3 *db = dbMap[dbName];
|
|
244
|
+
|
|
245
|
+
const char *errorMessage;
|
|
246
|
+
|
|
247
|
+
bool isConsuming = true;
|
|
248
|
+
bool isFailed = false;
|
|
249
|
+
|
|
250
|
+
int result = SQLITE_OK;
|
|
251
|
+
|
|
252
|
+
isConsuming = true;
|
|
253
|
+
|
|
254
|
+
int i, count, column_type;
|
|
255
|
+
std::string column_name, column_declared_type;
|
|
256
|
+
|
|
257
|
+
while (isConsuming) {
|
|
258
|
+
result = sqlite3_step(statement);
|
|
259
|
+
|
|
260
|
+
switch (result) {
|
|
261
|
+
case SQLITE_ROW: {
|
|
262
|
+
if (results == NULL) {
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
i = 0;
|
|
267
|
+
DumbHostObject row = DumbHostObject(metadatas);
|
|
268
|
+
|
|
269
|
+
count = sqlite3_column_count(statement);
|
|
270
|
+
|
|
271
|
+
while (i < count) {
|
|
272
|
+
column_type = sqlite3_column_type(statement, i);
|
|
142
273
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if (result.type == SQLiteError)
|
|
154
|
-
{
|
|
155
|
-
return {
|
|
156
|
-
.type = SQLiteError,
|
|
157
|
-
.message = mainDBName + " was unable to attach another database: " + std::string(result.message),
|
|
158
|
-
};
|
|
274
|
+
switch (column_type) {
|
|
275
|
+
case SQLITE_INTEGER: {
|
|
276
|
+
/**
|
|
277
|
+
* Warning this will loose precision because JS can
|
|
278
|
+
* only represent Integers up to 53 bits
|
|
279
|
+
*/
|
|
280
|
+
double column_value = sqlite3_column_double(statement, i);
|
|
281
|
+
row.values.push_back(JSVariant(column_value));
|
|
282
|
+
break;
|
|
159
283
|
}
|
|
160
|
-
return {
|
|
161
|
-
.type = SQLiteOk,
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
284
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
* */
|
|
170
|
-
std::string statement = "DETACH DATABASE " + alias;
|
|
171
|
-
BridgeResult result = sqliteExecuteLiteral(mainDBName, statement);
|
|
172
|
-
if (result.type == SQLiteError)
|
|
173
|
-
{
|
|
174
|
-
return BridgeResult{
|
|
175
|
-
.type = SQLiteError,
|
|
176
|
-
.message = mainDBName + "was unable to detach database: " + std::string(result.message),
|
|
177
|
-
};
|
|
285
|
+
case SQLITE_FLOAT: {
|
|
286
|
+
double column_value = sqlite3_column_double(statement, i);
|
|
287
|
+
row.values.push_back(JSVariant(column_value));
|
|
288
|
+
break;
|
|
178
289
|
}
|
|
179
|
-
return BridgeResult{
|
|
180
|
-
.type = SQLiteOk,
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
290
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
return closeResult;
|
|
192
|
-
}
|
|
291
|
+
case SQLITE_TEXT: {
|
|
292
|
+
const char *column_value =
|
|
293
|
+
reinterpret_cast<const char *>(sqlite3_column_text(statement, i));
|
|
294
|
+
int byteLen = sqlite3_column_bytes(statement, i);
|
|
295
|
+
// Specify length too; in case string contains NULL in the middle
|
|
296
|
+
row.values.push_back(JSVariant(std::string(column_value, byteLen)));
|
|
297
|
+
break;
|
|
193
298
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
299
|
+
|
|
300
|
+
case SQLITE_BLOB: {
|
|
301
|
+
int blob_size = sqlite3_column_bytes(statement, i);
|
|
302
|
+
const void *blob = sqlite3_column_blob(statement, i);
|
|
303
|
+
uint8_t *data = new uint8_t[blob_size];
|
|
304
|
+
// You cannot share raw memory between native and JS
|
|
305
|
+
// always copy the data
|
|
306
|
+
memcpy(data, blob, blob_size);
|
|
307
|
+
row.values.push_back(
|
|
308
|
+
JSVariant(ArrayBuffer{.data = std::shared_ptr<uint8_t>{data},
|
|
309
|
+
.size = static_cast<size_t>(blob_size)}));
|
|
310
|
+
break;
|
|
203
311
|
}
|
|
204
|
-
|
|
205
|
-
remove(dbPath.c_str());
|
|
206
|
-
|
|
207
|
-
return {
|
|
208
|
-
.type = SQLiteOk,
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
312
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
int sqIndex = ii + 1;
|
|
219
|
-
JSVariant value = values->at(ii);
|
|
220
|
-
|
|
221
|
-
if (std::holds_alternative<bool>(value))
|
|
222
|
-
{
|
|
223
|
-
sqlite3_bind_int(statement, sqIndex, std::get<bool>(value));
|
|
224
|
-
}
|
|
225
|
-
else if (std::holds_alternative<int>(value))
|
|
226
|
-
{
|
|
227
|
-
sqlite3_bind_int(statement, sqIndex, std::get<int>(value));
|
|
228
|
-
}
|
|
229
|
-
else if (std::holds_alternative<long long>(value))
|
|
230
|
-
{
|
|
231
|
-
sqlite3_bind_double(statement, sqIndex, std::get<long long>(value));
|
|
232
|
-
}
|
|
233
|
-
else if (std::holds_alternative<double>(value))
|
|
234
|
-
{
|
|
235
|
-
sqlite3_bind_double(statement, sqIndex, std::get<double>(value));
|
|
236
|
-
}
|
|
237
|
-
else if (std::holds_alternative<std::string>(value))
|
|
238
|
-
{
|
|
239
|
-
std::string str = std::get<std::string>(value);
|
|
240
|
-
sqlite3_bind_text(statement, sqIndex, str.c_str(), str.length(), SQLITE_TRANSIENT);
|
|
241
|
-
}
|
|
242
|
-
else if(std::holds_alternative<ArrayBuffer>(value))
|
|
243
|
-
{
|
|
244
|
-
ArrayBuffer buffer = std::get<ArrayBuffer>(value);
|
|
245
|
-
sqlite3_bind_blob(statement, sqIndex, buffer.data.get(), buffer.size, SQLITE_STATIC);
|
|
246
|
-
} else {
|
|
247
|
-
sqlite3_bind_null(statement, sqIndex);
|
|
248
|
-
}
|
|
313
|
+
case SQLITE_NULL:
|
|
314
|
+
// Intentionally left blank
|
|
315
|
+
|
|
316
|
+
default:
|
|
317
|
+
row.values.push_back(JSVariant(NULL));
|
|
318
|
+
break;
|
|
249
319
|
}
|
|
320
|
+
i++;
|
|
321
|
+
}
|
|
322
|
+
if (results != nullptr) {
|
|
323
|
+
results->push_back(row);
|
|
324
|
+
}
|
|
325
|
+
break;
|
|
250
326
|
}
|
|
251
327
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
328
|
+
case SQLITE_DONE:
|
|
329
|
+
if (metadatas != nullptr) {
|
|
330
|
+
i = 0;
|
|
331
|
+
count = sqlite3_column_count(statement);
|
|
332
|
+
|
|
333
|
+
while (i < count) {
|
|
334
|
+
column_name = sqlite3_column_name(statement, i);
|
|
335
|
+
const char *type = sqlite3_column_decltype(statement, i);
|
|
336
|
+
auto metadata = SmartHostObject();
|
|
337
|
+
metadata.fields.push_back(std::make_pair("name", column_name));
|
|
338
|
+
metadata.fields.push_back(std::make_pair("index", i));
|
|
339
|
+
metadata.fields.push_back(
|
|
340
|
+
std::make_pair("type", type == NULL ? "UNKNOWN" : type));
|
|
341
|
+
|
|
342
|
+
metadatas->push_back(metadata);
|
|
343
|
+
i++;
|
|
265
344
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
bool isFailed = false;
|
|
275
|
-
|
|
276
|
-
int result = SQLITE_OK;
|
|
277
|
-
|
|
278
|
-
do {
|
|
279
|
-
const char *queryStr = remainingStatement == nullptr ? query.c_str() : remainingStatement;
|
|
280
|
-
|
|
281
|
-
int statementStatus = sqlite3_prepare_v2(db, queryStr, -1, &statement, &remainingStatement);
|
|
282
|
-
|
|
283
|
-
if (statementStatus == SQLITE_ERROR)
|
|
284
|
-
{
|
|
285
|
-
const char *message = sqlite3_errmsg(db);
|
|
286
|
-
return {
|
|
287
|
-
.type = SQLiteError,
|
|
288
|
-
.message = "[op-sqlite] SQL statement error: " + std::string(message),
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
bindStatement(statement, params);
|
|
293
|
-
|
|
294
|
-
isConsuming = true;
|
|
295
|
-
|
|
296
|
-
int i, count, column_type;
|
|
297
|
-
std::string column_name, column_declared_type;
|
|
298
|
-
|
|
299
|
-
while (isConsuming)
|
|
300
|
-
{
|
|
301
|
-
result = sqlite3_step(statement);
|
|
302
|
-
|
|
303
|
-
switch (result)
|
|
304
|
-
{
|
|
305
|
-
case SQLITE_ROW: {
|
|
306
|
-
if(results == NULL)
|
|
307
|
-
{
|
|
308
|
-
break;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
i = 0;
|
|
312
|
-
DumbHostObject row = DumbHostObject(metadatas);
|
|
313
|
-
|
|
314
|
-
count = sqlite3_column_count(statement);
|
|
315
|
-
|
|
316
|
-
while (i < count)
|
|
317
|
-
{
|
|
318
|
-
column_type = sqlite3_column_type(statement, i);
|
|
319
|
-
|
|
320
|
-
switch (column_type)
|
|
321
|
-
{
|
|
322
|
-
case SQLITE_INTEGER:
|
|
323
|
-
{
|
|
324
|
-
/**
|
|
325
|
-
* Warning this will loose precision because JS can
|
|
326
|
-
* only represent Integers up to 53 bits
|
|
327
|
-
*/
|
|
328
|
-
double column_value = sqlite3_column_double(statement, i);
|
|
329
|
-
row.values.push_back(JSVariant(column_value));
|
|
330
|
-
break;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
case SQLITE_FLOAT:
|
|
334
|
-
{
|
|
335
|
-
double column_value = sqlite3_column_double(statement, i);
|
|
336
|
-
row.values.push_back(JSVariant(column_value));
|
|
337
|
-
break;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
case SQLITE_TEXT:
|
|
341
|
-
{
|
|
342
|
-
const char *column_value = reinterpret_cast<const char *>(sqlite3_column_text(statement, i));
|
|
343
|
-
int byteLen = sqlite3_column_bytes(statement, i);
|
|
344
|
-
// Specify length too; in case string contains NULL in the middle
|
|
345
|
-
row.values.push_back(JSVariant(std::string(column_value, byteLen)));
|
|
346
|
-
break;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
case SQLITE_BLOB:
|
|
350
|
-
{
|
|
351
|
-
int blob_size = sqlite3_column_bytes(statement, i);
|
|
352
|
-
const void *blob = sqlite3_column_blob(statement, i);
|
|
353
|
-
uint8_t *data = new uint8_t[blob_size];
|
|
354
|
-
// You cannot share raw memory between native and JS
|
|
355
|
-
// always copy the data
|
|
356
|
-
memcpy(data, blob, blob_size);
|
|
357
|
-
row.values.push_back(JSVariant(ArrayBuffer {
|
|
358
|
-
.data = std::shared_ptr<uint8_t>{data},
|
|
359
|
-
.size = static_cast<size_t>(blob_size)
|
|
360
|
-
}));
|
|
361
|
-
break;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
case SQLITE_NULL:
|
|
365
|
-
// Intentionally left blank
|
|
366
|
-
|
|
367
|
-
default:
|
|
368
|
-
row.values.push_back(JSVariant(NULL));
|
|
369
|
-
break;
|
|
370
|
-
}
|
|
371
|
-
i++;
|
|
372
|
-
}
|
|
373
|
-
if (results != nullptr) {
|
|
374
|
-
results->push_back(row);
|
|
375
|
-
}
|
|
376
|
-
break;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
case SQLITE_DONE:
|
|
380
|
-
if (metadatas != nullptr) {
|
|
381
|
-
i = 0;
|
|
382
|
-
count = sqlite3_column_count(statement);
|
|
383
|
-
|
|
384
|
-
while (i < count)
|
|
385
|
-
{
|
|
386
|
-
column_name = sqlite3_column_name(statement, i);
|
|
387
|
-
const char *type = sqlite3_column_decltype(statement, i);
|
|
388
|
-
auto metadata = DynamicHostObject();
|
|
389
|
-
metadata.fields.push_back(std::make_pair("name", column_name));
|
|
390
|
-
metadata.fields.push_back(std::make_pair("index", i));
|
|
391
|
-
metadata.fields.push_back(std::make_pair("type", type == NULL ? "UNKNOWN" : type));
|
|
392
|
-
|
|
393
|
-
metadatas->push_back(metadata);
|
|
394
|
-
i++;
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
isConsuming = false;
|
|
398
|
-
break;
|
|
399
|
-
|
|
400
|
-
default:
|
|
401
|
-
errorMessage = sqlite3_errmsg(db);
|
|
402
|
-
isFailed = true;
|
|
403
|
-
isConsuming = false;
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
sqlite3_finalize(statement);
|
|
408
|
-
} while (remainingStatement != NULL && strcmp(remainingStatement, "") != 0 && !isFailed);
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
if (isFailed)
|
|
412
|
-
{
|
|
413
|
-
|
|
414
|
-
return {
|
|
415
|
-
.type = SQLiteError,
|
|
416
|
-
.message = "[op-sqlite] SQLite code: " + std::to_string(result) + " execution error: " + std::string(errorMessage)
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
int changedRowCount = sqlite3_changes(db);
|
|
421
|
-
long long latestInsertRowId = sqlite3_last_insert_rowid(db);
|
|
422
|
-
|
|
423
|
-
return {
|
|
424
|
-
.type = SQLiteOk,
|
|
425
|
-
.affectedRows = changedRowCount,
|
|
426
|
-
.insertId = static_cast<double>(latestInsertRowId)
|
|
427
|
-
};
|
|
345
|
+
}
|
|
346
|
+
isConsuming = false;
|
|
347
|
+
break;
|
|
348
|
+
|
|
349
|
+
default:
|
|
350
|
+
errorMessage = sqlite3_errmsg(db);
|
|
351
|
+
isFailed = true;
|
|
352
|
+
isConsuming = false;
|
|
428
353
|
}
|
|
354
|
+
}
|
|
429
355
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
356
|
+
if (isFailed) {
|
|
357
|
+
|
|
358
|
+
return {.type = SQLiteError,
|
|
359
|
+
.message = "[op-sqlite] SQLite code: " + std::to_string(result) +
|
|
360
|
+
" execution error: " + std::string(errorMessage)};
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
int changedRowCount = sqlite3_changes(db);
|
|
364
|
+
long long latestInsertRowId = sqlite3_last_insert_rowid(db);
|
|
365
|
+
|
|
366
|
+
return {.type = SQLiteOk,
|
|
367
|
+
.affectedRows = changedRowCount,
|
|
368
|
+
.insertId = static_cast<double>(latestInsertRowId)};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
sqlite3_stmt *sqlite_prepare_statement(std::string const dbName,
|
|
372
|
+
std::string const &query) {
|
|
373
|
+
if (dbMap.find(dbName) == dbMap.end()) {
|
|
374
|
+
throw std::runtime_error("Database not opened");
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
sqlite3 *db = dbMap[dbName];
|
|
378
|
+
|
|
379
|
+
sqlite3_stmt *statement;
|
|
380
|
+
|
|
381
|
+
const char *queryStr = query.c_str();
|
|
382
|
+
|
|
383
|
+
int statementStatus = sqlite3_prepare_v2(db, queryStr, -1, &statement, NULL);
|
|
384
|
+
|
|
385
|
+
if (statementStatus == SQLITE_ERROR) {
|
|
386
|
+
const char *message = sqlite3_errmsg(db);
|
|
387
|
+
throw std::runtime_error("[op-sqlite] SQL statement error: " +
|
|
388
|
+
std::string(message));
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return statement;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
BridgeResult
|
|
395
|
+
sqliteExecute(std::string const dbName, std::string const &query,
|
|
396
|
+
const std::vector<JSVariant> *params,
|
|
397
|
+
std::vector<DumbHostObject> *results,
|
|
398
|
+
std::shared_ptr<std::vector<SmartHostObject>> metadatas) {
|
|
399
|
+
|
|
400
|
+
if (dbMap.find(dbName) == dbMap.end()) {
|
|
401
|
+
return {.type = SQLiteError,
|
|
402
|
+
.message = "[op-sqlite]: Database " + dbName + " is not open"};
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
sqlite3 *db = dbMap[dbName];
|
|
406
|
+
|
|
407
|
+
sqlite3_stmt *statement;
|
|
408
|
+
const char *errorMessage;
|
|
409
|
+
const char *remainingStatement = nullptr;
|
|
410
|
+
|
|
411
|
+
bool isConsuming = true;
|
|
412
|
+
bool isFailed = false;
|
|
413
|
+
|
|
414
|
+
int result = SQLITE_OK;
|
|
415
|
+
|
|
416
|
+
do {
|
|
417
|
+
const char *queryStr =
|
|
418
|
+
remainingStatement == nullptr ? query.c_str() : remainingStatement;
|
|
419
|
+
|
|
420
|
+
int statementStatus =
|
|
421
|
+
sqlite3_prepare_v2(db, queryStr, -1, &statement, &remainingStatement);
|
|
422
|
+
|
|
423
|
+
if (statementStatus == SQLITE_ERROR) {
|
|
424
|
+
const char *message = sqlite3_errmsg(db);
|
|
425
|
+
return {
|
|
426
|
+
.type = SQLiteError,
|
|
427
|
+
.message = "[op-sqlite] SQL statement error: " + std::string(message),
|
|
428
|
+
};
|
|
496
429
|
}
|
|
497
430
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
// Interrupt will make all pending operations to fail with SQLITE_INTERRUPT
|
|
501
|
-
// The ongoing work from threads will then fail ASAP
|
|
502
|
-
sqlite3_interrupt(x.second);
|
|
503
|
-
// Each DB connection can then be safely interrupted
|
|
504
|
-
sqlite3_close_v2(x.second);
|
|
505
|
-
}
|
|
506
|
-
dbMap.clear();
|
|
431
|
+
if (params != nullptr && params->size() > 0) {
|
|
432
|
+
bindStatement(statement, params);
|
|
507
433
|
}
|
|
508
434
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
throw std::invalid_argument("Uknown SQLite operation on hook");
|
|
435
|
+
isConsuming = true;
|
|
436
|
+
|
|
437
|
+
int i, count, column_type;
|
|
438
|
+
std::string column_name, column_declared_type;
|
|
439
|
+
|
|
440
|
+
while (isConsuming) {
|
|
441
|
+
result = sqlite3_step(statement);
|
|
442
|
+
|
|
443
|
+
switch (result) {
|
|
444
|
+
case SQLITE_ROW: {
|
|
445
|
+
if (results == NULL) {
|
|
446
|
+
break;
|
|
522
447
|
}
|
|
523
|
-
}
|
|
524
448
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
449
|
+
i = 0;
|
|
450
|
+
DumbHostObject row = DumbHostObject(metadatas);
|
|
451
|
+
|
|
452
|
+
count = sqlite3_column_count(statement);
|
|
453
|
+
|
|
454
|
+
while (i < count) {
|
|
455
|
+
column_type = sqlite3_column_type(statement, i);
|
|
456
|
+
|
|
457
|
+
switch (column_type) {
|
|
458
|
+
case SQLITE_INTEGER: {
|
|
459
|
+
/**
|
|
460
|
+
* Warning this will loose precision because JS can
|
|
461
|
+
* only represent Integers up to 53 bits
|
|
462
|
+
*/
|
|
463
|
+
double column_value = sqlite3_column_double(statement, i);
|
|
464
|
+
row.values.push_back(JSVariant(column_value));
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
case SQLITE_FLOAT: {
|
|
469
|
+
double column_value = sqlite3_column_double(statement, i);
|
|
470
|
+
row.values.push_back(JSVariant(column_value));
|
|
471
|
+
break;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
case SQLITE_TEXT: {
|
|
475
|
+
const char *column_value = reinterpret_cast<const char *>(
|
|
476
|
+
sqlite3_column_text(statement, i));
|
|
477
|
+
int byteLen = sqlite3_column_bytes(statement, i);
|
|
478
|
+
// Specify length too; in case string contains NULL in the middle
|
|
479
|
+
row.values.push_back(JSVariant(std::string(column_value, byteLen)));
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
case SQLITE_BLOB: {
|
|
484
|
+
int blob_size = sqlite3_column_bytes(statement, i);
|
|
485
|
+
const void *blob = sqlite3_column_blob(statement, i);
|
|
486
|
+
uint8_t *data = new uint8_t[blob_size];
|
|
487
|
+
// You cannot share raw memory between native and JS
|
|
488
|
+
// always copy the data
|
|
489
|
+
memcpy(data, blob, blob_size);
|
|
490
|
+
row.values.push_back(
|
|
491
|
+
JSVariant(ArrayBuffer{.data = std::shared_ptr<uint8_t>{data},
|
|
492
|
+
.size = static_cast<size_t>(blob_size)}));
|
|
493
|
+
break;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
case SQLITE_NULL:
|
|
497
|
+
// Intentionally left blank
|
|
498
|
+
|
|
499
|
+
default:
|
|
500
|
+
row.values.push_back(JSVariant(NULL));
|
|
501
|
+
break;
|
|
502
|
+
}
|
|
503
|
+
i++;
|
|
543
504
|
}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
updateCallbackMap[dbName] = callback;
|
|
547
|
-
const std::string *key = nullptr;
|
|
548
|
-
|
|
549
|
-
// TODO find a more elegant way to retrieve a reference to the key
|
|
550
|
-
for (auto const& element : dbMap) {
|
|
551
|
-
if(element.first == dbName) {
|
|
552
|
-
key = &element.first;
|
|
553
|
-
}
|
|
505
|
+
if (results != nullptr) {
|
|
506
|
+
results->push_back(row);
|
|
554
507
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
case SQLITE_DONE:
|
|
512
|
+
if (metadatas != nullptr) {
|
|
513
|
+
i = 0;
|
|
514
|
+
count = sqlite3_column_count(statement);
|
|
515
|
+
|
|
516
|
+
while (i < count) {
|
|
517
|
+
column_name = sqlite3_column_name(statement, i);
|
|
518
|
+
const char *type = sqlite3_column_decltype(statement, i);
|
|
519
|
+
auto metadata = SmartHostObject();
|
|
520
|
+
metadata.fields.push_back(std::make_pair("name", column_name));
|
|
521
|
+
metadata.fields.push_back(std::make_pair("index", i));
|
|
522
|
+
metadata.fields.push_back(
|
|
523
|
+
std::make_pair("type", type == NULL ? "UNKNOWN" : type));
|
|
524
|
+
|
|
525
|
+
metadatas->push_back(metadata);
|
|
526
|
+
i++;
|
|
527
|
+
}
|
|
573
528
|
}
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
return {
|
|
584
|
-
SQLiteOk
|
|
585
|
-
};
|
|
529
|
+
isConsuming = false;
|
|
530
|
+
break;
|
|
531
|
+
|
|
532
|
+
default:
|
|
533
|
+
errorMessage = sqlite3_errmsg(db);
|
|
534
|
+
isFailed = true;
|
|
535
|
+
isConsuming = false;
|
|
536
|
+
}
|
|
586
537
|
}
|
|
587
538
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
callback(strDbName);
|
|
592
|
-
// You need to return 0 to allow commits to continue
|
|
593
|
-
return 0;
|
|
594
|
-
}
|
|
539
|
+
sqlite3_finalize(statement);
|
|
540
|
+
} while (remainingStatement != NULL && strcmp(remainingStatement, "") != 0 &&
|
|
541
|
+
!isFailed);
|
|
595
542
|
|
|
596
|
-
|
|
597
|
-
std::function<void (std::string dbName)> const callback) {
|
|
598
|
-
if (dbMap.count(dbName) == 0)
|
|
599
|
-
{
|
|
600
|
-
return {
|
|
601
|
-
SQLiteError,
|
|
602
|
-
"[op-sqlite] Database not opened: " + dbName
|
|
603
|
-
};
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
sqlite3 *db = dbMap[dbName];
|
|
607
|
-
commitCallbackMap[dbName] = callback;
|
|
608
|
-
const std::string *key = nullptr;
|
|
609
|
-
|
|
610
|
-
// TODO find a more elegant way to retrieve a reference to the key
|
|
611
|
-
for (auto const& element : dbMap) {
|
|
612
|
-
if(element.first == dbName) {
|
|
613
|
-
key = &element.first;
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
sqlite3_commit_hook(
|
|
618
|
-
db,
|
|
619
|
-
&commit_callback,
|
|
620
|
-
(void *)key);
|
|
621
|
-
|
|
622
|
-
return {
|
|
623
|
-
SQLiteOk
|
|
624
|
-
};
|
|
625
|
-
}
|
|
543
|
+
if (isFailed) {
|
|
626
544
|
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
545
|
+
return {.type = SQLiteError,
|
|
546
|
+
.message = "[op-sqlite] SQLite code: " + std::to_string(result) +
|
|
547
|
+
" execution error: " + std::string(errorMessage)};
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
int changedRowCount = sqlite3_changes(db);
|
|
551
|
+
long long latestInsertRowId = sqlite3_last_insert_rowid(db);
|
|
552
|
+
|
|
553
|
+
return {.type = SQLiteOk,
|
|
554
|
+
.affectedRows = changedRowCount,
|
|
555
|
+
.insertId = static_cast<double>(latestInsertRowId)};
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
BridgeResult sqliteExecuteLiteral(std::string const dbName,
|
|
559
|
+
std::string const &query) {
|
|
560
|
+
if (dbMap.count(dbName) == 0) {
|
|
561
|
+
return {SQLiteError, "[op-sqlite] Database not opened: " + dbName};
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
sqlite3 *db = dbMap[dbName];
|
|
565
|
+
sqlite3_stmt *statement;
|
|
566
|
+
|
|
567
|
+
int statementStatus =
|
|
568
|
+
sqlite3_prepare_v2(db, query.c_str(), -1, &statement, NULL);
|
|
569
|
+
|
|
570
|
+
if (statementStatus != SQLITE_OK) {
|
|
571
|
+
const char *message = sqlite3_errmsg(db);
|
|
572
|
+
return {SQLiteError,
|
|
573
|
+
"[op-sqlite] SQL statement error: " + std::string(message), 0};
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
bool isConsuming = true;
|
|
577
|
+
bool isFailed = false;
|
|
578
|
+
|
|
579
|
+
int result;
|
|
580
|
+
std::string column_name;
|
|
581
|
+
|
|
582
|
+
while (isConsuming) {
|
|
583
|
+
result = sqlite3_step(statement);
|
|
584
|
+
|
|
585
|
+
switch (result) {
|
|
586
|
+
case SQLITE_ROW:
|
|
587
|
+
isConsuming = true;
|
|
588
|
+
break;
|
|
589
|
+
|
|
590
|
+
case SQLITE_DONE:
|
|
591
|
+
isConsuming = false;
|
|
592
|
+
break;
|
|
593
|
+
|
|
594
|
+
default:
|
|
595
|
+
isFailed = true;
|
|
596
|
+
isConsuming = false;
|
|
646
597
|
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
sqlite3_finalize(statement);
|
|
647
601
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
602
|
+
if (isFailed) {
|
|
603
|
+
const char *message = sqlite3_errmsg(db);
|
|
604
|
+
return {SQLiteError,
|
|
605
|
+
"[op-sqlite] SQL execution error: " + std::string(message), 0};
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
int changedRowCount = sqlite3_changes(db);
|
|
609
|
+
return {SQLiteOk, "", changedRowCount};
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
void sqliteCloseAll() {
|
|
613
|
+
for (auto const &x : dbMap) {
|
|
614
|
+
// Interrupt will make all pending operations to fail with SQLITE_INTERRUPT
|
|
615
|
+
// The ongoing work from threads will then fail ASAP
|
|
616
|
+
sqlite3_interrupt(x.second);
|
|
617
|
+
// Each DB connection can then be safely interrupted
|
|
618
|
+
sqlite3_close_v2(x.second);
|
|
619
|
+
}
|
|
620
|
+
dbMap.clear();
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
std::string operationToString(int operation_type) {
|
|
624
|
+
switch (operation_type) {
|
|
625
|
+
case SQLITE_INSERT:
|
|
626
|
+
return "INSERT";
|
|
627
|
+
|
|
628
|
+
case SQLITE_DELETE:
|
|
629
|
+
return "DELETE";
|
|
630
|
+
|
|
631
|
+
case SQLITE_UPDATE:
|
|
632
|
+
return "UPDATE";
|
|
633
|
+
|
|
634
|
+
default:
|
|
635
|
+
throw std::invalid_argument("Uknown SQLite operation on hook");
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
void update_callback(void *dbName, int operation_type, char const *database,
|
|
640
|
+
char const *table, sqlite3_int64 rowid) {
|
|
641
|
+
std::string &strDbName = *(static_cast<std::string *>(dbName));
|
|
642
|
+
auto callback = updateCallbackMap[strDbName];
|
|
643
|
+
callback(strDbName, std::string(table), operationToString(operation_type),
|
|
644
|
+
static_cast<int>(rowid));
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
BridgeResult registerUpdateHook(
|
|
648
|
+
std::string const dbName,
|
|
649
|
+
std::function<void(std::string dbName, std::string tableName,
|
|
650
|
+
std::string operation, int rowId)> const callback) {
|
|
651
|
+
if (dbMap.count(dbName) == 0) {
|
|
652
|
+
return {SQLiteError, "[op-sqlite] Database not opened: " + dbName};
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
sqlite3 *db = dbMap[dbName];
|
|
656
|
+
updateCallbackMap[dbName] = callback;
|
|
657
|
+
const std::string *key = nullptr;
|
|
658
|
+
|
|
659
|
+
// TODO find a more elegant way to retrieve a reference to the key
|
|
660
|
+
for (auto const &element : dbMap) {
|
|
661
|
+
if (element.first == dbName) {
|
|
662
|
+
key = &element.first;
|
|
652
663
|
}
|
|
664
|
+
}
|
|
653
665
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
666
|
+
sqlite3_update_hook(db, &update_callback, (void *)key);
|
|
667
|
+
|
|
668
|
+
return {SQLiteOk};
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
BridgeResult unregisterUpdateHook(std::string const dbName) {
|
|
672
|
+
if (dbMap.count(dbName) == 0) {
|
|
673
|
+
return {SQLiteError, "[op-sqlite] Database not opened: " + dbName};
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
sqlite3 *db = dbMap[dbName];
|
|
677
|
+
updateCallbackMap.erase(dbName);
|
|
678
|
+
|
|
679
|
+
sqlite3_update_hook(db, NULL, NULL);
|
|
680
|
+
|
|
681
|
+
return {SQLiteOk};
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
int commit_callback(void *dbName) {
|
|
685
|
+
std::string &strDbName = *(static_cast<std::string *>(dbName));
|
|
686
|
+
auto callback = commitCallbackMap[strDbName];
|
|
687
|
+
callback(strDbName);
|
|
688
|
+
// You need to return 0 to allow commits to continue
|
|
689
|
+
return 0;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
BridgeResult
|
|
693
|
+
registerCommitHook(std::string const dbName,
|
|
694
|
+
std::function<void(std::string dbName)> const callback) {
|
|
695
|
+
if (dbMap.count(dbName) == 0) {
|
|
696
|
+
return {SQLiteError, "[op-sqlite] Database not opened: " + dbName};
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
sqlite3 *db = dbMap[dbName];
|
|
700
|
+
commitCallbackMap[dbName] = callback;
|
|
701
|
+
const std::string *key = nullptr;
|
|
702
|
+
|
|
703
|
+
// TODO find a more elegant way to retrieve a reference to the key
|
|
704
|
+
for (auto const &element : dbMap) {
|
|
705
|
+
if (element.first == dbName) {
|
|
706
|
+
key = &element.first;
|
|
683
707
|
}
|
|
708
|
+
}
|
|
684
709
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
710
|
+
sqlite3_commit_hook(db, &commit_callback, (void *)key);
|
|
711
|
+
|
|
712
|
+
return {SQLiteOk};
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
BridgeResult unregisterCommitHook(std::string const dbName) {
|
|
716
|
+
if (dbMap.count(dbName) == 0) {
|
|
717
|
+
return {SQLiteError, "[op-sqlite] Database not opened: " + dbName};
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
sqlite3 *db = dbMap[dbName];
|
|
721
|
+
commitCallbackMap.erase(dbName);
|
|
722
|
+
sqlite3_commit_hook(db, NULL, NULL);
|
|
723
|
+
|
|
724
|
+
return {SQLiteOk};
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
void rollback_callback(void *dbName) {
|
|
728
|
+
std::string &strDbName = *(static_cast<std::string *>(dbName));
|
|
729
|
+
auto callback = rollbackCallbackMap[strDbName];
|
|
730
|
+
callback(strDbName);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
BridgeResult
|
|
734
|
+
registerRollbackHook(std::string const dbName,
|
|
735
|
+
std::function<void(std::string dbName)> const callback) {
|
|
736
|
+
if (dbMap.count(dbName) == 0) {
|
|
737
|
+
return {SQLiteError, "[op-sqlite] Database not opened: " + dbName};
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
sqlite3 *db = dbMap[dbName];
|
|
741
|
+
rollbackCallbackMap[dbName] = callback;
|
|
742
|
+
const std::string *key = nullptr;
|
|
743
|
+
|
|
744
|
+
// TODO find a more elegant way to retrieve a reference to the key
|
|
745
|
+
for (auto const &element : dbMap) {
|
|
746
|
+
if (element.first == dbName) {
|
|
747
|
+
key = &element.first;
|
|
705
748
|
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
sqlite3_rollback_hook(db, &rollback_callback, (void *)key);
|
|
752
|
+
|
|
753
|
+
return {SQLiteOk};
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
BridgeResult unregisterRollbackHook(std::string const dbName) {
|
|
757
|
+
if (dbMap.count(dbName) == 0) {
|
|
758
|
+
return {SQLiteError, "[op-sqlite] Database not opened: " + dbName};
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
sqlite3 *db = dbMap[dbName];
|
|
762
|
+
rollbackCallbackMap.erase(dbName);
|
|
763
|
+
|
|
764
|
+
sqlite3_rollback_hook(db, NULL, NULL);
|
|
765
|
+
|
|
766
|
+
return {SQLiteOk};
|
|
706
767
|
}
|
|
768
|
+
} // namespace opsqlite
|