@op-engineering/op-sqlite 11.2.4 → 11.2.6
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/android/CMakeLists.txt +5 -16
- package/android/build.gradle +3 -3
- package/android/cpp-adapter.cpp +1 -1
- package/android/src/main/java/com/op/sqlite/OPSQLiteBridge.kt +1 -1
- package/android/src/main/java/com/op/sqlite/OPSQLiteModule.kt +1 -1
- package/cpp/DBHostObject.cpp +286 -374
- package/cpp/DBHostObject.h +29 -16
- package/cpp/DumbHostObject.cpp +5 -5
- package/cpp/DumbHostObject.h +8 -11
- package/cpp/PreparedStatementHostObject.cpp +54 -20
- package/cpp/PreparedStatementHostObject.h +15 -11
- package/cpp/SmartHostObject.cpp +5 -4
- package/cpp/SmartHostObject.h +4 -7
- package/cpp/ThreadPool.cpp +9 -8
- package/cpp/ThreadPool.h +4 -7
- package/cpp/bindings.cpp +14 -25
- package/cpp/bindings.h +3 -2
- package/cpp/bridge.cpp +146 -365
- package/cpp/bridge.h +30 -42
- package/cpp/libsql/bridge.cpp +65 -164
- package/cpp/libsql/bridge.h +25 -22
- package/cpp/logs.h +2 -0
- package/cpp/macros.h +8 -4
- package/cpp/sqlite3.c +5622 -2808
- package/cpp/sqlite3.h +180 -22
- package/cpp/types.h +1 -8
- package/cpp/utils.cpp +78 -67
- package/cpp/utils.h +9 -13
- package/ios/OPSQLite.mm +1 -1
- package/lib/commonjs/index.js +4 -5
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/index.js +4 -5
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +16 -13
package/cpp/bridge.cpp
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
+
// This file contains pure sqlite operations without JSI interaction
|
|
2
|
+
// Allows a clear defined boundary between the JSI and the SQLite operations
|
|
3
|
+
// so that threading operations are safe and contained within DBHostObject
|
|
4
|
+
|
|
1
5
|
#include "bridge.h"
|
|
6
|
+
#include "DBHostObject.h"
|
|
2
7
|
#include "DumbHostObject.h"
|
|
3
8
|
#include "SmartHostObject.h"
|
|
4
9
|
#include "logs.h"
|
|
5
10
|
#include "utils.h"
|
|
6
11
|
#include <iostream>
|
|
7
12
|
#include <sstream>
|
|
13
|
+
#include <stdexcept>
|
|
8
14
|
#include <unordered_map>
|
|
9
15
|
#include <variant>
|
|
10
16
|
|
|
@@ -16,33 +22,42 @@
|
|
|
16
22
|
|
|
17
23
|
namespace opsqlite {
|
|
18
24
|
|
|
19
|
-
|
|
20
|
-
std::
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
std::unordered_map<std::string, UpdateCallback> updateCallbackMap =
|
|
24
|
-
std::unordered_map<std::string, UpdateCallback>();
|
|
25
|
+
inline void opsqlite_bind_statement(sqlite3_stmt *statement,
|
|
26
|
+
const std::vector<JSVariant> *values) {
|
|
27
|
+
sqlite3_clear_bindings(statement);
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
std::unordered_map<std::string, CommitCallback>();
|
|
29
|
+
size_t size = values->size();
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
for (int ii = 0; ii < size; ii++) {
|
|
32
|
+
int stmt_index = ii + 1;
|
|
33
|
+
JSVariant value = values->at(ii);
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
std::visit(
|
|
36
|
+
[&](auto &&v) {
|
|
37
|
+
using T = std::decay_t<decltype(v)>;
|
|
38
|
+
|
|
39
|
+
if constexpr (std::is_same_v<T, bool>) {
|
|
40
|
+
sqlite3_bind_int(statement, stmt_index, static_cast<int>(v));
|
|
41
|
+
} else if constexpr (std::is_same_v<T, int>) {
|
|
42
|
+
sqlite3_bind_int(statement, stmt_index, v);
|
|
43
|
+
} else if constexpr (std::is_same_v<T, long long>) {
|
|
44
|
+
sqlite3_bind_double(statement, stmt_index, static_cast<double>(v));
|
|
45
|
+
} else if constexpr (std::is_same_v<T, double>) {
|
|
46
|
+
sqlite3_bind_double(statement, stmt_index, v);
|
|
47
|
+
} else if constexpr (std::is_same_v<T, std::string>) {
|
|
48
|
+
sqlite3_bind_text(statement, stmt_index, v.c_str(),
|
|
49
|
+
static_cast<int>(v.length()), SQLITE_TRANSIENT);
|
|
50
|
+
} else if constexpr (std::is_same_v<T, ArrayBuffer>) {
|
|
51
|
+
sqlite3_bind_blob(statement, stmt_index, v.data.get(),
|
|
52
|
+
static_cast<int>(v.size), SQLITE_TRANSIENT);
|
|
53
|
+
} else {
|
|
54
|
+
sqlite3_bind_null(statement, stmt_index);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
value);
|
|
36
58
|
}
|
|
37
59
|
}
|
|
38
60
|
|
|
39
|
-
// _____ _____
|
|
40
|
-
// /\ | __ \_ _|
|
|
41
|
-
// / \ | |__) || |
|
|
42
|
-
// / /\ \ | ___/ | |
|
|
43
|
-
// / ____ \| | _| |_
|
|
44
|
-
// /_/ \_\_| |_____|
|
|
45
|
-
|
|
46
61
|
/// Returns the completely formed db path, but it also creates any sub-folders
|
|
47
62
|
/// along the way
|
|
48
63
|
std::string opsqlite_get_db_path(std::string const &db_name,
|
|
@@ -57,47 +72,35 @@ std::string opsqlite_get_db_path(std::string const &db_name,
|
|
|
57
72
|
}
|
|
58
73
|
|
|
59
74
|
#ifdef OP_SQLITE_USE_SQLCIPHER
|
|
60
|
-
|
|
61
|
-
std::string const &last_path,
|
|
75
|
+
sqlite3 *opsqlite_open(std::string const &name, std::string const &path,
|
|
62
76
|
std::string const &crsqlite_path,
|
|
63
77
|
std::string const &sqlite_vec_path,
|
|
64
|
-
std::string const &
|
|
78
|
+
std::string const &encryption_key) {
|
|
65
79
|
#else
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
std::string const &sqlite_vec_path) {
|
|
70
|
-
|
|
71
|
-
if (dbMap.count(name) != 0) {
|
|
72
|
-
throw std::runtime_error(
|
|
73
|
-
"[OP-SQLITE] Only one connection per database is allowed, db name: " +
|
|
74
|
-
name);
|
|
75
|
-
}
|
|
80
|
+
sqlite3 *opsqlite_open(std::string const &name, std::string const &path,
|
|
81
|
+
[[maybe_unused]] std::string const &crsqlite_path,
|
|
82
|
+
[[maybe_unused]] std::string const &sqlite_vec_path) {
|
|
76
83
|
#endif
|
|
77
|
-
std::string
|
|
84
|
+
std::string final_path = opsqlite_get_db_path(name, path);
|
|
85
|
+
char *errMsg;
|
|
86
|
+
sqlite3 *db;
|
|
78
87
|
|
|
79
|
-
int
|
|
88
|
+
int flags =
|
|
80
89
|
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX;
|
|
81
90
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
int status = sqlite3_open_v2(dbPath.c_str(), &db, sqlOpenFlags, nullptr);
|
|
91
|
+
int status = sqlite3_open_v2(final_path.c_str(), &db, flags, nullptr);
|
|
85
92
|
|
|
86
93
|
if (status != SQLITE_OK) {
|
|
87
|
-
|
|
94
|
+
throw std::runtime_error(sqlite3_errmsg(db));
|
|
88
95
|
}
|
|
89
96
|
|
|
90
|
-
dbMap[name] = db;
|
|
91
|
-
|
|
92
97
|
#ifdef OP_SQLITE_USE_SQLCIPHER
|
|
93
|
-
opsqlite_execute(
|
|
98
|
+
opsqlite_execute(db, "PRAGMA key = '" + encryption_key + "'", nullptr);
|
|
94
99
|
#endif
|
|
95
100
|
|
|
96
101
|
#ifndef OP_SQLITE_USE_PHONE_VERSION
|
|
97
102
|
sqlite3_enable_load_extension(db, 1);
|
|
98
103
|
#endif
|
|
99
|
-
|
|
100
|
-
char *errMsg;
|
|
101
104
|
|
|
102
105
|
#ifdef OP_SQLITE_USE_CRSQLITE
|
|
103
106
|
const char *crsqliteEntryPoint = "sqlite3_crsqlite_init";
|
|
@@ -106,7 +109,7 @@ BridgeResult opsqlite_open(std::string const &name,
|
|
|
106
109
|
&errMsg);
|
|
107
110
|
|
|
108
111
|
if (errMsg != nullptr) {
|
|
109
|
-
|
|
112
|
+
throw std::runtime_error(errMsg);
|
|
110
113
|
}
|
|
111
114
|
#endif
|
|
112
115
|
|
|
@@ -116,138 +119,57 @@ BridgeResult opsqlite_open(std::string const &name,
|
|
|
116
119
|
sqlite3_load_extension(db, sqlite_vec_path.c_str(), vec_entry_point, &errMsg);
|
|
117
120
|
|
|
118
121
|
if (errMsg != nullptr) {
|
|
119
|
-
|
|
122
|
+
throw std::runtime_error(errMsg);
|
|
120
123
|
}
|
|
121
124
|
#endif
|
|
122
125
|
|
|
123
126
|
TOKENIZER_LIST
|
|
124
127
|
|
|
125
|
-
return
|
|
128
|
+
return db;
|
|
126
129
|
}
|
|
127
130
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
check_db_open(name);
|
|
131
|
-
|
|
132
|
-
sqlite3 *db = dbMap[name];
|
|
131
|
+
void opsqlite_close(sqlite3 *db) {
|
|
133
132
|
|
|
134
133
|
#ifdef OP_SQLITE_USE_CRSQLITE
|
|
135
134
|
opsqlite_execute(name, "select crsql_finalize();", nullptr);
|
|
136
135
|
#endif
|
|
137
136
|
|
|
138
137
|
sqlite3_close_v2(db);
|
|
139
|
-
|
|
140
|
-
dbMap.erase(name);
|
|
141
|
-
|
|
142
|
-
return BridgeResult{
|
|
143
|
-
.type = SQLiteOk,
|
|
144
|
-
};
|
|
145
138
|
}
|
|
146
139
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (result.type == SQLiteError) {
|
|
157
|
-
return {
|
|
158
|
-
.type = SQLiteError,
|
|
159
|
-
.message = mainDBName + " was unable to attach another database: " +
|
|
160
|
-
std::string(result.message),
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
return {
|
|
164
|
-
.type = SQLiteOk,
|
|
165
|
-
};
|
|
140
|
+
void opsqlite_attach(sqlite3 *db, std::string const &main_db_name,
|
|
141
|
+
std::string const &doc_path,
|
|
142
|
+
std::string const &secondary_db_name,
|
|
143
|
+
std::string const &alias) {
|
|
144
|
+
auto secondary_db_path = opsqlite_get_db_path(secondary_db_name, doc_path);
|
|
145
|
+
auto statement = "ATTACH DATABASE '" + secondary_db_path + "' AS " + alias;
|
|
146
|
+
|
|
147
|
+
opsqlite_execute(db, statement, nullptr);
|
|
166
148
|
}
|
|
167
149
|
|
|
168
|
-
|
|
169
|
-
|
|
150
|
+
void opsqlite_detach(sqlite3 *db, std::string const &main_db_name,
|
|
151
|
+
std::string const &alias) {
|
|
170
152
|
std::string statement = "DETACH DATABASE " + alias;
|
|
171
|
-
|
|
172
|
-
if (result.type == SQLiteError) {
|
|
173
|
-
return BridgeResult{
|
|
174
|
-
.type = SQLiteError,
|
|
175
|
-
.message = mainDBName + "was unable to detach database: " +
|
|
176
|
-
std::string(result.message),
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
return BridgeResult{
|
|
180
|
-
.type = SQLiteOk,
|
|
181
|
-
};
|
|
153
|
+
opsqlite_execute(db, statement, nullptr);
|
|
182
154
|
}
|
|
183
155
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
BridgeResult closeResult = opsqlite_close(dbName);
|
|
188
|
-
if (closeResult.type == SQLiteError) {
|
|
189
|
-
return closeResult;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
156
|
+
void opsqlite_remove(sqlite3 *db, std::string const &name,
|
|
157
|
+
std::string const &doc_path) {
|
|
158
|
+
opsqlite_close(db);
|
|
192
159
|
|
|
193
|
-
std::string
|
|
160
|
+
std::string db_path = opsqlite_get_db_path(name, doc_path);
|
|
194
161
|
|
|
195
|
-
if (!file_exists(
|
|
196
|
-
|
|
197
|
-
.message = "[op-sqlite]: Database file not found" + dbPath};
|
|
162
|
+
if (!file_exists(db_path)) {
|
|
163
|
+
throw std::runtime_error("op-sqlite: db file not found:" + db_path);
|
|
198
164
|
}
|
|
199
165
|
|
|
200
|
-
remove(
|
|
201
|
-
|
|
202
|
-
return {
|
|
203
|
-
.type = SQLiteOk,
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
inline void opsqlite_bind_statement(sqlite3_stmt *statement,
|
|
208
|
-
const std::vector<JSVariant> *values) {
|
|
209
|
-
// reset any existing bound values
|
|
210
|
-
sqlite3_clear_bindings(statement);
|
|
211
|
-
|
|
212
|
-
size_t size = values->size();
|
|
213
|
-
|
|
214
|
-
for (int ii = 0; ii < size; ii++) {
|
|
215
|
-
int sqIndex = ii + 1;
|
|
216
|
-
JSVariant value = values->at(ii);
|
|
217
|
-
|
|
218
|
-
if (std::holds_alternative<bool>(value)) {
|
|
219
|
-
sqlite3_bind_int(statement, sqIndex,
|
|
220
|
-
static_cast<int>(std::get<bool>(value)));
|
|
221
|
-
} else if (std::holds_alternative<int>(value)) {
|
|
222
|
-
sqlite3_bind_int(statement, sqIndex, std::get<int>(value));
|
|
223
|
-
} else if (std::holds_alternative<long long>(value)) {
|
|
224
|
-
sqlite3_bind_double(statement, sqIndex,
|
|
225
|
-
static_cast<double>(std::get<long long>(value)));
|
|
226
|
-
} else if (std::holds_alternative<double>(value)) {
|
|
227
|
-
sqlite3_bind_double(statement, sqIndex, std::get<double>(value));
|
|
228
|
-
} else if (std::holds_alternative<std::string>(value)) {
|
|
229
|
-
std::string str = std::get<std::string>(value);
|
|
230
|
-
sqlite3_bind_text(statement, sqIndex, str.c_str(),
|
|
231
|
-
static_cast<int>(str.length()), SQLITE_TRANSIENT);
|
|
232
|
-
} else if (std::holds_alternative<ArrayBuffer>(value)) {
|
|
233
|
-
ArrayBuffer buffer = std::get<ArrayBuffer>(value);
|
|
234
|
-
sqlite3_bind_blob(statement, sqIndex, buffer.data.get(),
|
|
235
|
-
static_cast<int>(buffer.size), SQLITE_TRANSIENT);
|
|
236
|
-
} else {
|
|
237
|
-
sqlite3_bind_null(statement, sqIndex);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
166
|
+
remove(db_path.c_str());
|
|
240
167
|
}
|
|
241
168
|
|
|
242
169
|
BridgeResult opsqlite_execute_prepared_statement(
|
|
243
|
-
|
|
244
|
-
std::vector<DumbHostObject> *results,
|
|
170
|
+
sqlite3 *db, sqlite3_stmt *statement, std::vector<DumbHostObject> *results,
|
|
245
171
|
std::shared_ptr<std::vector<SmartHostObject>> &metadatas) {
|
|
246
172
|
|
|
247
|
-
check_db_open(dbName);
|
|
248
|
-
|
|
249
|
-
sqlite3 *db = dbMap[dbName];
|
|
250
|
-
|
|
251
173
|
const char *errorMessage;
|
|
252
174
|
|
|
253
175
|
bool isConsuming = true;
|
|
@@ -356,25 +278,20 @@ BridgeResult opsqlite_execute_prepared_statement(
|
|
|
356
278
|
sqlite3_reset(statement);
|
|
357
279
|
|
|
358
280
|
if (isFailed) {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
281
|
+
throw std::runtime_error(
|
|
282
|
+
"[op-sqlite] SQLite code: " + std::to_string(result) +
|
|
283
|
+
" execution error: " + std::string(errorMessage));
|
|
362
284
|
}
|
|
363
285
|
|
|
364
286
|
int changedRowCount = sqlite3_changes(db);
|
|
365
287
|
long long latestInsertRowId = sqlite3_last_insert_rowid(db);
|
|
366
288
|
|
|
367
|
-
return {.
|
|
368
|
-
.affectedRows = changedRowCount,
|
|
289
|
+
return {.affectedRows = changedRowCount,
|
|
369
290
|
.insertId = static_cast<double>(latestInsertRowId)};
|
|
370
291
|
}
|
|
371
292
|
|
|
372
|
-
sqlite3_stmt *opsqlite_prepare_statement(
|
|
293
|
+
sqlite3_stmt *opsqlite_prepare_statement(sqlite3 *db,
|
|
373
294
|
std::string const &query) {
|
|
374
|
-
check_db_open(dbName);
|
|
375
|
-
|
|
376
|
-
sqlite3 *db = dbMap[dbName];
|
|
377
|
-
|
|
378
295
|
sqlite3_stmt *statement;
|
|
379
296
|
|
|
380
297
|
const char *queryStr = query.c_str();
|
|
@@ -391,12 +308,8 @@ sqlite3_stmt *opsqlite_prepare_statement(std::string const &dbName,
|
|
|
391
308
|
return statement;
|
|
392
309
|
}
|
|
393
310
|
|
|
394
|
-
BridgeResult opsqlite_execute(
|
|
311
|
+
BridgeResult opsqlite_execute(sqlite3 *db, std::string const &query,
|
|
395
312
|
const std::vector<JSVariant> *params) {
|
|
396
|
-
check_db_open(name);
|
|
397
|
-
|
|
398
|
-
sqlite3 *db = dbMap[name];
|
|
399
|
-
|
|
400
313
|
sqlite3_stmt *statement;
|
|
401
314
|
const char *errorMessage = nullptr;
|
|
402
315
|
const char *remainingStatement = nullptr;
|
|
@@ -417,10 +330,8 @@ BridgeResult opsqlite_execute(std::string const &name, std::string const &query,
|
|
|
417
330
|
|
|
418
331
|
if (status != SQLITE_OK) {
|
|
419
332
|
errorMessage = sqlite3_errmsg(db);
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
"[op-sqlite] SQL prepare error: " + std::string(errorMessage),
|
|
423
|
-
.affectedRows = 0};
|
|
333
|
+
throw std::runtime_error("[op-sqlite] SQL prepare error: " +
|
|
334
|
+
std::string(errorMessage));
|
|
424
335
|
}
|
|
425
336
|
|
|
426
337
|
// The statement did not fail to parse but there is nothing to do, just
|
|
@@ -516,32 +427,23 @@ BridgeResult opsqlite_execute(std::string const &name, std::string const &query,
|
|
|
516
427
|
|
|
517
428
|
if (has_failed) {
|
|
518
429
|
const char *message = sqlite3_errmsg(db);
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
"[op-sqlite] SQL execution error: " + std::string(message),
|
|
522
|
-
.affectedRows = 0,
|
|
523
|
-
.insertId = 0};
|
|
430
|
+
throw std::runtime_error("[op-sqlite] statement execution error: " +
|
|
431
|
+
std::string(message));
|
|
524
432
|
}
|
|
525
433
|
|
|
526
434
|
int changedRowCount = sqlite3_changes(db);
|
|
527
435
|
long long latestInsertRowId = sqlite3_last_insert_rowid(db);
|
|
528
|
-
return {.
|
|
529
|
-
.affectedRows = changedRowCount,
|
|
436
|
+
return {.affectedRows = changedRowCount,
|
|
530
437
|
.insertId = static_cast<double>(latestInsertRowId),
|
|
531
438
|
.rows = std::move(rows),
|
|
532
439
|
.column_names = std::move(column_names)};
|
|
533
440
|
}
|
|
534
441
|
|
|
535
|
-
/// Base execution function, returns HostObjects to the JS environment
|
|
536
442
|
BridgeResult opsqlite_execute_host_objects(
|
|
537
|
-
std::string const &
|
|
538
|
-
|
|
443
|
+
sqlite3 *db, std::string const &query, const std::vector<JSVariant> *params,
|
|
444
|
+
std::vector<DumbHostObject> *results,
|
|
539
445
|
std::shared_ptr<std::vector<SmartHostObject>> &metadatas) {
|
|
540
446
|
|
|
541
|
-
check_db_open(dbName);
|
|
542
|
-
|
|
543
|
-
sqlite3 *db = dbMap[dbName];
|
|
544
|
-
|
|
545
447
|
sqlite3_stmt *statement;
|
|
546
448
|
const char *errorMessage;
|
|
547
449
|
const char *remainingStatement = nullptr;
|
|
@@ -560,11 +462,10 @@ BridgeResult opsqlite_execute_host_objects(
|
|
|
560
462
|
|
|
561
463
|
if (statementStatus != SQLITE_OK) {
|
|
562
464
|
const char *message = sqlite3_errmsg(db);
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
std::string(message)};
|
|
465
|
+
throw std::runtime_error(
|
|
466
|
+
"[op-sqlite] SQL statement error on opsqlite_execute:\n" +
|
|
467
|
+
std::to_string(statementStatus) + " description:\n" +
|
|
468
|
+
std::string(message));
|
|
568
469
|
}
|
|
569
470
|
|
|
570
471
|
// The statement did not fail to parse but there is nothing to do, just
|
|
@@ -683,32 +584,24 @@ BridgeResult opsqlite_execute_host_objects(
|
|
|
683
584
|
strcmp(remainingStatement, "") != 0 && !isFailed);
|
|
684
585
|
|
|
685
586
|
if (isFailed) {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
"[op-sqlite] SQLite error code: " + std::to_string(result) +
|
|
690
|
-
", description: " + std::string(errorMessage)};
|
|
587
|
+
throw std::runtime_error(
|
|
588
|
+
"[op-sqlite] SQLite error code: " + std::to_string(result) +
|
|
589
|
+
", description: " + std::string(errorMessage));
|
|
691
590
|
}
|
|
692
591
|
|
|
693
592
|
int changedRowCount = sqlite3_changes(db);
|
|
694
593
|
long long latestInsertRowId = sqlite3_last_insert_rowid(db);
|
|
695
594
|
|
|
696
|
-
return {.
|
|
697
|
-
.affectedRows = changedRowCount,
|
|
595
|
+
return {.affectedRows = changedRowCount,
|
|
698
596
|
.insertId = static_cast<double>(latestInsertRowId)};
|
|
699
597
|
}
|
|
700
598
|
|
|
701
599
|
/// Executes returning data in raw arrays, a small performance optimization
|
|
702
600
|
/// for certain use cases
|
|
703
601
|
BridgeResult
|
|
704
|
-
opsqlite_execute_raw(
|
|
602
|
+
opsqlite_execute_raw(sqlite3 *db, std::string const &query,
|
|
705
603
|
const std::vector<JSVariant> *params,
|
|
706
604
|
std::vector<std::vector<JSVariant>> *results) {
|
|
707
|
-
|
|
708
|
-
check_db_open(dbName);
|
|
709
|
-
|
|
710
|
-
sqlite3 *db = dbMap[dbName];
|
|
711
|
-
|
|
712
605
|
sqlite3_stmt *statement;
|
|
713
606
|
const char *errorMessage;
|
|
714
607
|
const char *remainingStatement = nullptr;
|
|
@@ -727,12 +620,9 @@ opsqlite_execute_raw(std::string const &dbName, std::string const &query,
|
|
|
727
620
|
|
|
728
621
|
if (statementStatus != SQLITE_OK) {
|
|
729
622
|
const char *message = sqlite3_errmsg(db);
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
std::to_string(statementStatus) +
|
|
734
|
-
" description:" + std::string(message),
|
|
735
|
-
};
|
|
623
|
+
throw std::runtime_error(
|
|
624
|
+
"[op-sqlite] SQL statement error:" + std::to_string(statementStatus) +
|
|
625
|
+
" description:" + std::string(message));
|
|
736
626
|
}
|
|
737
627
|
|
|
738
628
|
// The statement did not fail to parse but there is nothing to do, just
|
|
@@ -825,35 +715,18 @@ opsqlite_execute_raw(std::string const &dbName, std::string const &query,
|
|
|
825
715
|
strcmp(remainingStatement, "") != 0 && !isFailed);
|
|
826
716
|
|
|
827
717
|
if (isFailed) {
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
"[op-sqlite] SQLite error code: " + std::to_string(step) +
|
|
832
|
-
", description: " + std::string(errorMessage)};
|
|
718
|
+
throw std::runtime_error(
|
|
719
|
+
"[op-sqlite] SQLite error code: " + std::to_string(step) +
|
|
720
|
+
", description: " + std::string(errorMessage));
|
|
833
721
|
}
|
|
834
722
|
|
|
835
723
|
int changedRowCount = sqlite3_changes(db);
|
|
836
724
|
long long latestInsertRowId = sqlite3_last_insert_rowid(db);
|
|
837
725
|
|
|
838
|
-
return {.
|
|
839
|
-
.affectedRows = changedRowCount,
|
|
726
|
+
return {.affectedRows = changedRowCount,
|
|
840
727
|
.insertId = static_cast<double>(latestInsertRowId)};
|
|
841
728
|
}
|
|
842
729
|
|
|
843
|
-
void opsqlite_close_all() {
|
|
844
|
-
for (auto const &x : dbMap) {
|
|
845
|
-
// Interrupt will make all pending operations to fail with
|
|
846
|
-
// SQLITE_INTERRUPT The ongoing work from threads will then fail ASAP
|
|
847
|
-
sqlite3_interrupt(x.second);
|
|
848
|
-
// Each DB connection can then be safely interrupted
|
|
849
|
-
sqlite3_close_v2(x.second);
|
|
850
|
-
}
|
|
851
|
-
dbMap.clear();
|
|
852
|
-
updateCallbackMap.clear();
|
|
853
|
-
rollbackCallbackMap.clear();
|
|
854
|
-
commitCallbackMap.clear();
|
|
855
|
-
}
|
|
856
|
-
|
|
857
730
|
std::string operation_to_string(int operation_type) {
|
|
858
731
|
switch (operation_type) {
|
|
859
732
|
case SQLITE_INSERT:
|
|
@@ -866,139 +739,64 @@ std::string operation_to_string(int operation_type) {
|
|
|
866
739
|
return "UPDATE";
|
|
867
740
|
|
|
868
741
|
default:
|
|
869
|
-
throw std::
|
|
742
|
+
throw std::runtime_error("Unknown SQLite operation on hook");
|
|
870
743
|
}
|
|
871
744
|
}
|
|
872
745
|
|
|
873
|
-
void update_callback(void *
|
|
746
|
+
void update_callback(void *db_host_object_ptr, int operation_type,
|
|
874
747
|
[[maybe_unused]] char const *database, char const *table,
|
|
875
748
|
sqlite3_int64 row_id) {
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
static_cast<int>(row_id));
|
|
749
|
+
auto db_host_object = reinterpret_cast<DBHostObject *>(db_host_object_ptr);
|
|
750
|
+
db_host_object->on_update(std::string(table),
|
|
751
|
+
operation_to_string(operation_type), row_id);
|
|
880
752
|
}
|
|
881
753
|
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
check_db_open(dbName);
|
|
885
|
-
|
|
886
|
-
sqlite3 *db = dbMap[dbName];
|
|
887
|
-
updateCallbackMap[dbName] = callback;
|
|
888
|
-
const std::string *key = nullptr;
|
|
889
|
-
|
|
890
|
-
// TODO find a more elegant way to retrieve a reference to the key
|
|
891
|
-
for (auto const &element : dbMap) {
|
|
892
|
-
if (element.first == dbName) {
|
|
893
|
-
key = &element.first;
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
sqlite3_update_hook(db, &update_callback, (void *)key);
|
|
898
|
-
|
|
899
|
-
return {SQLiteOk};
|
|
754
|
+
void opsqlite_register_update_hook(sqlite3 *db, void *db_host_object) {
|
|
755
|
+
sqlite3_update_hook(db, &update_callback, (void *)db_host_object);
|
|
900
756
|
}
|
|
901
757
|
|
|
902
|
-
|
|
903
|
-
check_db_open(dbName);
|
|
904
|
-
|
|
905
|
-
sqlite3 *db = dbMap[dbName];
|
|
906
|
-
updateCallbackMap.erase(dbName);
|
|
907
|
-
|
|
758
|
+
void opsqlite_deregister_update_hook(sqlite3 *db) {
|
|
908
759
|
sqlite3_update_hook(db, nullptr, nullptr);
|
|
909
|
-
|
|
910
|
-
return {SQLiteOk};
|
|
911
760
|
}
|
|
912
761
|
|
|
913
|
-
int commit_callback(void *
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
callback(strDbName);
|
|
917
|
-
// You need to return 0 to allow commits to continue
|
|
762
|
+
int commit_callback(void *db_host_object_ptr) {
|
|
763
|
+
auto db_host_object = reinterpret_cast<DBHostObject *>(db_host_object_ptr);
|
|
764
|
+
db_host_object->on_commit();
|
|
918
765
|
return 0;
|
|
919
766
|
}
|
|
920
767
|
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
check_db_open(dbName);
|
|
924
|
-
|
|
925
|
-
sqlite3 *db = dbMap[dbName];
|
|
926
|
-
commitCallbackMap[dbName] = callback;
|
|
927
|
-
const std::string *key = nullptr;
|
|
928
|
-
|
|
929
|
-
// TODO find a more elegant way to retrieve a reference to the key
|
|
930
|
-
for (auto const &element : dbMap) {
|
|
931
|
-
if (element.first == dbName) {
|
|
932
|
-
key = &element.first;
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
|
-
|
|
936
|
-
sqlite3_commit_hook(db, &commit_callback, (void *)key);
|
|
937
|
-
|
|
938
|
-
return {SQLiteOk};
|
|
768
|
+
void opsqlite_register_commit_hook(sqlite3 *db, void *db_host_object_ptr) {
|
|
769
|
+
sqlite3_commit_hook(db, &commit_callback, db_host_object_ptr);
|
|
939
770
|
}
|
|
940
771
|
|
|
941
|
-
|
|
942
|
-
check_db_open(dbName);
|
|
943
|
-
|
|
944
|
-
sqlite3 *db = dbMap[dbName];
|
|
945
|
-
commitCallbackMap.erase(dbName);
|
|
772
|
+
void opsqlite_deregister_commit_hook(sqlite3 *db) {
|
|
946
773
|
sqlite3_commit_hook(db, nullptr, nullptr);
|
|
947
|
-
|
|
948
|
-
return {SQLiteOk};
|
|
949
774
|
}
|
|
950
775
|
|
|
951
|
-
void rollback_callback(void *
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
callback(strDbName);
|
|
776
|
+
void rollback_callback(void *db_host_object_ptr) {
|
|
777
|
+
auto db_host_object = reinterpret_cast<DBHostObject *>(db_host_object_ptr);
|
|
778
|
+
db_host_object->on_rollback();
|
|
955
779
|
}
|
|
956
780
|
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
check_db_open(dbName);
|
|
960
|
-
|
|
961
|
-
sqlite3 *db = dbMap[dbName];
|
|
962
|
-
rollbackCallbackMap[dbName] = callback;
|
|
963
|
-
const std::string *key = nullptr;
|
|
964
|
-
|
|
965
|
-
// TODO find a more elegant way to retrieve a reference to the key
|
|
966
|
-
for (auto const &element : dbMap) {
|
|
967
|
-
if (element.first == dbName) {
|
|
968
|
-
key = &element.first;
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
sqlite3_rollback_hook(db, &rollback_callback, (void *)key);
|
|
973
|
-
|
|
974
|
-
return {SQLiteOk};
|
|
781
|
+
void opsqlite_register_rollback_hook(sqlite3 *db, void *db_host_object_ptr) {
|
|
782
|
+
sqlite3_rollback_hook(db, &rollback_callback, db_host_object_ptr);
|
|
975
783
|
}
|
|
976
784
|
|
|
977
|
-
|
|
978
|
-
check_db_open(dbName);
|
|
979
|
-
|
|
980
|
-
sqlite3 *db = dbMap[dbName];
|
|
981
|
-
rollbackCallbackMap.erase(dbName);
|
|
982
|
-
|
|
785
|
+
void opsqlite_deregister_rollback_hook(sqlite3 *db) {
|
|
983
786
|
sqlite3_rollback_hook(db, nullptr, nullptr);
|
|
984
|
-
|
|
985
|
-
return {SQLiteOk};
|
|
986
787
|
}
|
|
987
788
|
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
std::string &entry_point) {
|
|
789
|
+
void opsqlite_load_extension(sqlite3 *db, std::string &path,
|
|
790
|
+
std::string &entry_point) {
|
|
991
791
|
#ifdef OP_SQLITE_USE_PHONE_VERSION
|
|
992
|
-
throw std::runtime_error(
|
|
993
|
-
|
|
792
|
+
throw std::runtime_error("[op-sqlite] Embedded version of SQLite does not "
|
|
793
|
+
"support loading extensions");
|
|
994
794
|
#else
|
|
995
|
-
check_db_open(db_name);
|
|
996
|
-
|
|
997
|
-
sqlite3 *db = dbMap[db_name];
|
|
998
795
|
int status = 0;
|
|
999
796
|
status = sqlite3_enable_load_extension(db, 1);
|
|
797
|
+
|
|
1000
798
|
if (status != SQLITE_OK) {
|
|
1001
|
-
|
|
799
|
+
throw std::runtime_error("Could not enable extension loading");
|
|
1002
800
|
}
|
|
1003
801
|
|
|
1004
802
|
const char *entry_point_cstr = nullptr;
|
|
@@ -1011,54 +809,37 @@ BridgeResult opsqlite_load_extension(std::string const &db_name,
|
|
|
1011
809
|
status = sqlite3_load_extension(db, path.c_str(), entry_point_cstr,
|
|
1012
810
|
&error_message);
|
|
1013
811
|
if (status != SQLITE_OK) {
|
|
1014
|
-
|
|
812
|
+
throw std::runtime_error(error_message);
|
|
1015
813
|
}
|
|
1016
|
-
|
|
1017
|
-
return {SQLiteOk};
|
|
1018
814
|
#endif
|
|
1019
815
|
}
|
|
1020
816
|
|
|
1021
|
-
BatchResult opsqlite_execute_batch(
|
|
817
|
+
BatchResult opsqlite_execute_batch(sqlite3 *db,
|
|
1022
818
|
std::vector<BatchArguments> *commands) {
|
|
1023
819
|
size_t commandCount = commands->size();
|
|
1024
820
|
if (commandCount <= 0) {
|
|
1025
|
-
|
|
1026
|
-
.type = SQLiteError,
|
|
1027
|
-
.message = "No SQL commands provided",
|
|
1028
|
-
};
|
|
821
|
+
throw std::runtime_error("No SQL commands provided");
|
|
1029
822
|
}
|
|
1030
823
|
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
auto result = opsqlite_execute(
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
.message = result.message,
|
|
1044
|
-
};
|
|
1045
|
-
} else {
|
|
1046
|
-
affectedRows += result.affectedRows;
|
|
1047
|
-
}
|
|
824
|
+
int affectedRows = 0;
|
|
825
|
+
opsqlite_execute(db, "BEGIN EXCLUSIVE TRANSACTION", nullptr);
|
|
826
|
+
for (int i = 0; i < commandCount; i++) {
|
|
827
|
+
const auto &command = commands->at(i);
|
|
828
|
+
// We do not provide a datastructure to receive query data because we
|
|
829
|
+
// don't need/want to handle this results in a batch execution
|
|
830
|
+
try {
|
|
831
|
+
auto result = opsqlite_execute(db, command.sql, command.params.get());
|
|
832
|
+
affectedRows += result.affectedRows;
|
|
833
|
+
} catch (std::exception &exc) {
|
|
834
|
+
opsqlite_execute(db, "ROLLBACK", nullptr);
|
|
835
|
+
throw exc;
|
|
1048
836
|
}
|
|
1049
|
-
opsqlite_execute(name, "COMMIT", nullptr);
|
|
1050
|
-
return BatchResult{
|
|
1051
|
-
.type = SQLiteOk,
|
|
1052
|
-
.affectedRows = affectedRows,
|
|
1053
|
-
.commands = static_cast<int>(commandCount),
|
|
1054
|
-
};
|
|
1055
|
-
} catch (std::exception &exc) {
|
|
1056
|
-
opsqlite_execute(name, "ROLLBACK", nullptr);
|
|
1057
|
-
return BatchResult{
|
|
1058
|
-
.type = SQLiteError,
|
|
1059
|
-
.message = exc.what(),
|
|
1060
|
-
};
|
|
1061
837
|
}
|
|
838
|
+
opsqlite_execute(db, "COMMIT", nullptr);
|
|
839
|
+
return BatchResult{
|
|
840
|
+
.affectedRows = affectedRows,
|
|
841
|
+
.commands = static_cast<int>(commandCount),
|
|
842
|
+
};
|
|
1062
843
|
}
|
|
1063
844
|
|
|
1064
845
|
} // namespace opsqlite
|