@op-engineering/op-sqlite 15.0.6 → 15.1.0
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 +1 -1
- package/android/build.gradle +1 -1
- package/android/cpp-adapter.cpp +1 -1
- package/android/jniLibs/arm64-v8a/libsql_experimental.a +0 -0
- package/android/jniLibs/armeabi-v7a/libsql_experimental.a +0 -0
- package/android/jniLibs/x86/libsql_experimental.a +0 -0
- package/android/jniLibs/x86_64/libsql_experimental.a +0 -0
- package/android/src/main/java/com/op/sqlite/OPSQLiteModule.kt +7 -9
- package/cpp/DBHostObject.cpp +470 -660
- package/cpp/DBHostObject.h +56 -58
- package/cpp/DumbHostObject.cpp +1 -1
- package/cpp/DumbHostObject.h +12 -13
- package/cpp/OPSqlite.cpp +207 -0
- package/cpp/OPThreadPool.cpp +79 -79
- package/cpp/OPThreadPool.h +28 -28
- package/cpp/PreparedStatementHostObject.cpp +87 -136
- package/cpp/PreparedStatementHostObject.h +16 -28
- package/cpp/SmartHostObject.cpp +1 -1
- package/cpp/SmartHostObject.h +6 -7
- package/cpp/bridge.cpp +639 -633
- package/cpp/bridge.h +2 -2
- package/cpp/libsql/LICENSE.txt +9 -0
- package/cpp/libsql/bridge.cpp +25 -2
- package/cpp/libsql/{bridge.h → bridge.hpp} +8 -4
- package/cpp/libsql/libsql.h +4 -0
- package/cpp/macros.hpp +21 -0
- package/cpp/sqlcipher/LICENSE.txt +24 -0
- package/cpp/types.hpp +42 -0
- package/cpp/utils.cpp +320 -255
- package/cpp/{utils.h → utils.hpp} +9 -1
- package/ios/OPSQLite.mm +104 -106
- package/ios/libsql.xcframework/Info.plist +5 -5
- package/ios/libsql.xcframework/ios-arm64/Headers/libsql.h +4 -0
- package/ios/libsql.xcframework/ios-arm64/libsql_experimental.a +0 -0
- package/ios/libsql.xcframework/ios-arm64_x86_64-simulator/Headers/libsql.h +4 -0
- package/ios/libsql.xcframework/ios-arm64_x86_64-simulator/libsql_experimental.a +0 -0
- package/lib/module/functions.js +42 -33
- package/lib/module/functions.js.map +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/functions.d.ts +5 -1
- package/lib/typescript/src/functions.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +9 -1
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/op-sqlite.podspec +1 -1
- package/package.json +10 -8
- package/src/functions.ts +54 -43
- package/src/index.ts +1 -12
- package/src/types.ts +9 -1
- package/cpp/bindings.cpp +0 -202
- package/cpp/macros.h +0 -15
- package/cpp/types.h +0 -33
- /package/cpp/{bindings.h → OPSqlite.hpp} +0 -0
package/cpp/bridge.cpp
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
#include "DumbHostObject.h"
|
|
8
8
|
#include "SmartHostObject.h"
|
|
9
9
|
#include "logs.h"
|
|
10
|
-
#include "utils.
|
|
10
|
+
#include "utils.hpp"
|
|
11
11
|
#include <filesystem>
|
|
12
12
|
#include <iostream>
|
|
13
13
|
#include <sstream>
|
|
@@ -25,42 +25,38 @@ namespace opsqlite {
|
|
|
25
25
|
|
|
26
26
|
inline void opsqlite_bind_statement(sqlite3_stmt *statement,
|
|
27
27
|
const std::vector<JSVariant> *values) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
value);
|
|
63
|
-
}
|
|
28
|
+
sqlite3_clear_bindings(statement);
|
|
29
|
+
|
|
30
|
+
size_t size = values->size();
|
|
31
|
+
|
|
32
|
+
for (int ii = 0; ii < size; ii++) {
|
|
33
|
+
int stmt_index = ii + 1;
|
|
34
|
+
JSVariant value = values->at(ii);
|
|
35
|
+
|
|
36
|
+
std::visit(
|
|
37
|
+
[&](auto &&v) {
|
|
38
|
+
using T = std::decay_t<decltype(v)>;
|
|
39
|
+
|
|
40
|
+
if constexpr (std::is_same_v<T, bool>) {
|
|
41
|
+
sqlite3_bind_int(statement, stmt_index, static_cast<int>(v));
|
|
42
|
+
} else if constexpr (std::is_same_v<T, int>) {
|
|
43
|
+
sqlite3_bind_int(statement, stmt_index, v);
|
|
44
|
+
} else if constexpr (std::is_same_v<T, long long>) {
|
|
45
|
+
sqlite3_bind_double(statement, stmt_index, static_cast<double>(v));
|
|
46
|
+
} else if constexpr (std::is_same_v<T, double>) {
|
|
47
|
+
sqlite3_bind_double(statement, stmt_index, v);
|
|
48
|
+
} else if constexpr (std::is_same_v<T, std::string>) {
|
|
49
|
+
sqlite3_bind_text(statement, stmt_index, v.c_str(),
|
|
50
|
+
static_cast<int>(v.length()), SQLITE_TRANSIENT);
|
|
51
|
+
} else if constexpr (std::is_same_v<T, ArrayBuffer>) {
|
|
52
|
+
sqlite3_bind_blob(statement, stmt_index, v.data.get(),
|
|
53
|
+
static_cast<int>(v.size), SQLITE_TRANSIENT);
|
|
54
|
+
} else {
|
|
55
|
+
sqlite3_bind_null(statement, stmt_index);
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
value);
|
|
59
|
+
}
|
|
64
60
|
}
|
|
65
61
|
|
|
66
62
|
/// Returns the completely formed db path, but it also creates any sub-folders
|
|
@@ -68,18 +64,18 @@ inline void opsqlite_bind_statement(sqlite3_stmt *statement,
|
|
|
68
64
|
std::string opsqlite_get_db_path(std::string const &db_name,
|
|
69
65
|
std::string const &location) {
|
|
70
66
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
67
|
+
if (location == ":memory:") {
|
|
68
|
+
return location;
|
|
69
|
+
}
|
|
74
70
|
|
|
75
|
-
|
|
76
|
-
|
|
71
|
+
// Will return false if the directory already exists, no need to check
|
|
72
|
+
std::filesystem::create_directories(location);
|
|
77
73
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
74
|
+
if (!location.empty() && location.back() != '/') {
|
|
75
|
+
return location + "/" + db_name;
|
|
76
|
+
}
|
|
81
77
|
|
|
82
|
-
|
|
78
|
+
return location + db_name;
|
|
83
79
|
}
|
|
84
80
|
|
|
85
81
|
#ifdef OP_SQLITE_USE_SQLCIPHER
|
|
@@ -92,369 +88,386 @@ sqlite3 *opsqlite_open(std::string const &name, std::string const &path,
|
|
|
92
88
|
[[maybe_unused]] std::string const &crsqlite_path,
|
|
93
89
|
[[maybe_unused]] std::string const &sqlite_vec_path) {
|
|
94
90
|
#endif
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
91
|
+
std::string final_path = opsqlite_get_db_path(name, path);
|
|
92
|
+
char *errMsg;
|
|
93
|
+
sqlite3 *db;
|
|
98
94
|
|
|
99
|
-
|
|
100
|
-
|
|
95
|
+
int flags =
|
|
96
|
+
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX;
|
|
101
97
|
|
|
102
|
-
|
|
98
|
+
int status = sqlite3_open_v2(final_path.c_str(), &db, flags, nullptr);
|
|
103
99
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
100
|
+
if (status != SQLITE_OK) {
|
|
101
|
+
throw std::runtime_error(sqlite3_errmsg(db));
|
|
102
|
+
}
|
|
107
103
|
|
|
108
104
|
#ifdef OP_SQLITE_USE_SQLCIPHER
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
105
|
+
if (!encryption_key.empty()) {
|
|
106
|
+
opsqlite_execute(db, "PRAGMA key = '" + encryption_key + "'", nullptr);
|
|
107
|
+
}
|
|
112
108
|
#endif
|
|
113
109
|
|
|
114
110
|
#ifndef OP_SQLITE_USE_PHONE_VERSION
|
|
115
|
-
|
|
111
|
+
sqlite3_enable_load_extension(db, 1);
|
|
116
112
|
#endif
|
|
117
113
|
|
|
118
114
|
#ifdef OP_SQLITE_USE_CRSQLITE
|
|
119
|
-
|
|
115
|
+
const char *crsqliteEntryPoint = "sqlite3_crsqlite_init";
|
|
120
116
|
|
|
121
|
-
|
|
122
|
-
|
|
117
|
+
sqlite3_load_extension(db, crsqlite_path.c_str(), crsqliteEntryPoint,
|
|
118
|
+
&errMsg);
|
|
123
119
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
120
|
+
if (errMsg != nullptr) {
|
|
121
|
+
throw std::runtime_error(errMsg);
|
|
122
|
+
}
|
|
127
123
|
#endif
|
|
128
124
|
|
|
129
125
|
#ifdef OP_SQLITE_USE_SQLITE_VEC
|
|
130
|
-
|
|
126
|
+
const char *vec_entry_point = "sqlite3_vec_init";
|
|
131
127
|
|
|
132
|
-
|
|
133
|
-
&errMsg);
|
|
128
|
+
sqlite3_load_extension(db, sqlite_vec_path.c_str(), vec_entry_point, &errMsg);
|
|
134
129
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
130
|
+
if (errMsg != nullptr) {
|
|
131
|
+
throw std::runtime_error(errMsg);
|
|
132
|
+
}
|
|
138
133
|
#endif
|
|
139
134
|
|
|
140
|
-
|
|
135
|
+
TOKENIZER_LIST
|
|
141
136
|
|
|
142
|
-
|
|
137
|
+
return db;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
void create_dirs_if_needed(const std::string &path) {
|
|
141
|
+
if (path == ":memory:") {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Extract directory part from path (exclude filename)
|
|
146
|
+
std::filesystem::path fs_path(path);
|
|
147
|
+
auto dir = fs_path.parent_path();
|
|
148
|
+
if (!dir.empty()) {
|
|
149
|
+
std::filesystem::create_directories(dir);
|
|
150
|
+
}
|
|
143
151
|
}
|
|
144
152
|
|
|
145
153
|
void opsqlite_close(sqlite3 *db) {
|
|
146
154
|
#ifdef OP_SQLITE_USE_CRSQLITE
|
|
147
|
-
|
|
155
|
+
opsqlite_execute(db, "select crsql_finalize();", nullptr);
|
|
148
156
|
#endif
|
|
149
157
|
|
|
150
|
-
|
|
158
|
+
sqlite3_close_v2(db);
|
|
151
159
|
}
|
|
152
160
|
|
|
153
161
|
void opsqlite_attach(sqlite3 *db, std::string const &doc_path,
|
|
154
162
|
std::string const &secondary_db_name,
|
|
155
163
|
std::string const &alias) {
|
|
156
|
-
|
|
157
|
-
|
|
164
|
+
auto secondary_db_path = opsqlite_get_db_path(secondary_db_name, doc_path);
|
|
165
|
+
auto statement = "ATTACH DATABASE '" + secondary_db_path + "' AS " + alias;
|
|
158
166
|
|
|
159
|
-
|
|
167
|
+
opsqlite_execute(db, statement, nullptr);
|
|
160
168
|
}
|
|
161
169
|
|
|
162
170
|
void opsqlite_detach(sqlite3 *db, std::string const &alias) {
|
|
163
|
-
|
|
164
|
-
|
|
171
|
+
std::string statement = "DETACH DATABASE " + alias;
|
|
172
|
+
opsqlite_execute(db, statement, nullptr);
|
|
165
173
|
}
|
|
166
174
|
|
|
167
175
|
void opsqlite_remove(sqlite3 *db, std::string const &name,
|
|
168
176
|
std::string const &doc_path) {
|
|
169
|
-
|
|
177
|
+
opsqlite_close(db);
|
|
170
178
|
|
|
171
|
-
|
|
179
|
+
std::string db_path = opsqlite_get_db_path(name, doc_path);
|
|
172
180
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
181
|
+
if (!file_exists(db_path)) {
|
|
182
|
+
throw std::runtime_error("op-sqlite: db file not found:" + db_path);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
remove(db_path.c_str());
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
void opsqlite_remove_v2(sqlite3 *db, std::string const &path) {
|
|
189
|
+
opsqlite_close(db);
|
|
176
190
|
|
|
177
|
-
|
|
191
|
+
if (!file_exists(path)) {
|
|
192
|
+
throw std::runtime_error("[op-sqlite] db file not found: " + path);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
remove(path.c_str());
|
|
178
196
|
}
|
|
179
197
|
|
|
180
198
|
BridgeResult opsqlite_execute_prepared_statement(
|
|
181
199
|
sqlite3 *db, sqlite3_stmt *statement, std::vector<DumbHostObject> *results,
|
|
182
200
|
std::shared_ptr<std::vector<SmartHostObject>> &metadatas) {
|
|
183
201
|
|
|
184
|
-
|
|
202
|
+
const char *errorMessage;
|
|
185
203
|
|
|
186
|
-
|
|
187
|
-
|
|
204
|
+
bool isConsuming = true;
|
|
205
|
+
bool isFailed = false;
|
|
188
206
|
|
|
189
|
-
|
|
207
|
+
int result = SQLITE_OK;
|
|
190
208
|
|
|
191
|
-
|
|
192
|
-
|
|
209
|
+
int i, count, column_type;
|
|
210
|
+
std::string column_name, column_declared_type;
|
|
193
211
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
switch (result) {
|
|
198
|
-
case SQLITE_ROW: {
|
|
199
|
-
i = 0;
|
|
200
|
-
DumbHostObject row = DumbHostObject(metadatas);
|
|
201
|
-
|
|
202
|
-
count = sqlite3_column_count(statement);
|
|
203
|
-
|
|
204
|
-
while (i < count) {
|
|
205
|
-
column_type = sqlite3_column_type(statement, i);
|
|
206
|
-
|
|
207
|
-
switch (column_type) {
|
|
208
|
-
case SQLITE_INTEGER: {
|
|
209
|
-
/**
|
|
210
|
-
* Warning this will loose precision because JS can
|
|
211
|
-
* only represent Integers up to 53 bits
|
|
212
|
-
*/
|
|
213
|
-
double column_value = sqlite3_column_double(statement, i);
|
|
214
|
-
row.values.emplace_back(column_value);
|
|
215
|
-
break;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
case SQLITE_FLOAT: {
|
|
219
|
-
double column_value = sqlite3_column_double(statement, i);
|
|
220
|
-
row.values.emplace_back(column_value);
|
|
221
|
-
break;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
case SQLITE_TEXT: {
|
|
225
|
-
const char *column_value = reinterpret_cast<const char *>(
|
|
226
|
-
sqlite3_column_text(statement, i));
|
|
227
|
-
int byteLen = sqlite3_column_bytes(statement, i);
|
|
228
|
-
// Specify length too; in case string contains NULL in the
|
|
229
|
-
// middle
|
|
230
|
-
row.values.emplace_back(std::string(column_value, byteLen));
|
|
231
|
-
break;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
case SQLITE_BLOB: {
|
|
235
|
-
int blob_size = sqlite3_column_bytes(statement, i);
|
|
236
|
-
const void *blob = sqlite3_column_blob(statement, i);
|
|
237
|
-
auto *data = new uint8_t[blob_size];
|
|
238
|
-
// You cannot share raw memory between native and JS
|
|
239
|
-
// always copy the data
|
|
240
|
-
memcpy(data, blob, blob_size);
|
|
241
|
-
row.values.emplace_back(
|
|
242
|
-
ArrayBuffer{.data = std::shared_ptr<uint8_t>{data},
|
|
243
|
-
.size = static_cast<size_t>(blob_size)});
|
|
244
|
-
break;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
case SQLITE_NULL:
|
|
248
|
-
// Intentionally left blank
|
|
249
|
-
|
|
250
|
-
default:
|
|
251
|
-
row.values.emplace_back(nullptr);
|
|
252
|
-
break;
|
|
253
|
-
}
|
|
254
|
-
i++;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
results->emplace_back(row);
|
|
212
|
+
while (isConsuming) {
|
|
213
|
+
result = sqlite3_step(statement);
|
|
258
214
|
|
|
259
|
-
|
|
215
|
+
switch (result) {
|
|
216
|
+
case SQLITE_ROW: {
|
|
217
|
+
i = 0;
|
|
218
|
+
DumbHostObject row = DumbHostObject(metadatas);
|
|
219
|
+
|
|
220
|
+
count = sqlite3_column_count(statement);
|
|
221
|
+
|
|
222
|
+
while (i < count) {
|
|
223
|
+
column_type = sqlite3_column_type(statement, i);
|
|
224
|
+
|
|
225
|
+
switch (column_type) {
|
|
226
|
+
case SQLITE_INTEGER: {
|
|
227
|
+
/**
|
|
228
|
+
* Warning this will loose precision because JS can
|
|
229
|
+
* only represent Integers up to 53 bits
|
|
230
|
+
*/
|
|
231
|
+
double column_value = sqlite3_column_double(statement, i);
|
|
232
|
+
row.values.emplace_back(column_value);
|
|
233
|
+
break;
|
|
260
234
|
}
|
|
261
235
|
|
|
262
|
-
case
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
236
|
+
case SQLITE_FLOAT: {
|
|
237
|
+
double column_value = sqlite3_column_double(statement, i);
|
|
238
|
+
row.values.emplace_back(column_value);
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
case SQLITE_TEXT: {
|
|
243
|
+
const char *column_value =
|
|
244
|
+
reinterpret_cast<const char *>(sqlite3_column_text(statement, i));
|
|
245
|
+
int byteLen = sqlite3_column_bytes(statement, i);
|
|
246
|
+
// Specify length too; in case string contains NULL in the
|
|
247
|
+
// middle
|
|
248
|
+
row.values.emplace_back(std::string(column_value, byteLen));
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
case SQLITE_BLOB: {
|
|
253
|
+
int blob_size = sqlite3_column_bytes(statement, i);
|
|
254
|
+
const void *blob = sqlite3_column_blob(statement, i);
|
|
255
|
+
auto *data = new uint8_t[blob_size];
|
|
256
|
+
// You cannot share raw memory between native and JS
|
|
257
|
+
// always copy the data
|
|
258
|
+
memcpy(data, blob, blob_size);
|
|
259
|
+
row.values.emplace_back(
|
|
260
|
+
ArrayBuffer{.data = std::shared_ptr<uint8_t>{data},
|
|
261
|
+
.size = static_cast<size_t>(blob_size)});
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
case SQLITE_NULL:
|
|
266
|
+
// Intentionally left blank
|
|
282
267
|
|
|
283
268
|
default:
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
isConsuming = false;
|
|
269
|
+
row.values.emplace_back(nullptr);
|
|
270
|
+
break;
|
|
287
271
|
}
|
|
272
|
+
i++;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
results->emplace_back(row);
|
|
276
|
+
|
|
277
|
+
break;
|
|
288
278
|
}
|
|
289
279
|
|
|
290
|
-
|
|
280
|
+
case SQLITE_DONE:
|
|
281
|
+
if (metadatas != nullptr) {
|
|
282
|
+
i = 0;
|
|
283
|
+
count = sqlite3_column_count(statement);
|
|
284
|
+
|
|
285
|
+
while (i < count) {
|
|
286
|
+
column_name = sqlite3_column_name(statement, i);
|
|
287
|
+
const char *type = sqlite3_column_decltype(statement, i);
|
|
288
|
+
auto metadata = SmartHostObject();
|
|
289
|
+
metadata.fields.emplace_back("name", column_name);
|
|
290
|
+
metadata.fields.emplace_back("index", i);
|
|
291
|
+
metadata.fields.emplace_back("type",
|
|
292
|
+
type == nullptr ? "UNKNOWN" : type);
|
|
293
|
+
|
|
294
|
+
metadatas->emplace_back(metadata);
|
|
295
|
+
i++;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
isConsuming = false;
|
|
299
|
+
break;
|
|
291
300
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
301
|
+
default:
|
|
302
|
+
errorMessage = sqlite3_errmsg(db);
|
|
303
|
+
isFailed = true;
|
|
304
|
+
isConsuming = false;
|
|
296
305
|
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
sqlite3_reset(statement);
|
|
309
|
+
|
|
310
|
+
if (isFailed) {
|
|
311
|
+
throw std::runtime_error(
|
|
312
|
+
"[op-sqlite] SQLite code: " + std::to_string(result) +
|
|
313
|
+
" execution error: " + std::string(errorMessage));
|
|
314
|
+
}
|
|
297
315
|
|
|
298
|
-
|
|
299
|
-
|
|
316
|
+
int changedRowCount = sqlite3_changes(db);
|
|
317
|
+
long long latestInsertRowId = sqlite3_last_insert_rowid(db);
|
|
300
318
|
|
|
301
|
-
|
|
302
|
-
|
|
319
|
+
return {.affectedRows = changedRowCount,
|
|
320
|
+
.insertId = static_cast<double>(latestInsertRowId)};
|
|
303
321
|
}
|
|
304
322
|
|
|
305
323
|
sqlite3_stmt *opsqlite_prepare_statement(sqlite3 *db,
|
|
306
324
|
std::string const &query) {
|
|
307
|
-
|
|
325
|
+
sqlite3_stmt *statement;
|
|
308
326
|
|
|
309
|
-
|
|
327
|
+
const char *queryStr = query.c_str();
|
|
310
328
|
|
|
311
|
-
|
|
312
|
-
|
|
329
|
+
int statementStatus =
|
|
330
|
+
sqlite3_prepare_v2(db, queryStr, -1, &statement, nullptr);
|
|
313
331
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
332
|
+
if (statementStatus == SQLITE_ERROR) {
|
|
333
|
+
const char *message = sqlite3_errmsg(db);
|
|
334
|
+
throw std::runtime_error("[op-sqlite] SQL prepare statement error: " +
|
|
335
|
+
std::string(message));
|
|
336
|
+
}
|
|
319
337
|
|
|
320
|
-
|
|
338
|
+
return statement;
|
|
321
339
|
}
|
|
322
340
|
|
|
323
341
|
BridgeResult opsqlite_execute(sqlite3 *db, std::string const &query,
|
|
324
342
|
const std::vector<JSVariant> *params) {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
if (status != SQLITE_OK) {
|
|
344
|
-
errorMessage = sqlite3_errmsg(db);
|
|
345
|
-
throw std::runtime_error("[op-sqlite] sqlite query error: " +
|
|
346
|
-
std::string(errorMessage));
|
|
347
|
-
}
|
|
343
|
+
sqlite3_stmt *statement;
|
|
344
|
+
const char *errorMessage = nullptr;
|
|
345
|
+
const char *remainingStatement = nullptr;
|
|
346
|
+
bool has_failed = false;
|
|
347
|
+
int status, current_column, column_count, column_type;
|
|
348
|
+
std::string column_name, column_declared_type;
|
|
349
|
+
std::vector<std::string> column_names;
|
|
350
|
+
std::vector<std::vector<JSVariant>> rows;
|
|
351
|
+
rows.reserve(20);
|
|
352
|
+
std::vector<JSVariant> row;
|
|
353
|
+
|
|
354
|
+
do {
|
|
355
|
+
const char *query_str =
|
|
356
|
+
remainingStatement == nullptr ? query.c_str() : remainingStatement;
|
|
357
|
+
|
|
358
|
+
status =
|
|
359
|
+
sqlite3_prepare_v2(db, query_str, -1, &statement, &remainingStatement);
|
|
348
360
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
361
|
+
if (status != SQLITE_OK) {
|
|
362
|
+
errorMessage = sqlite3_errmsg(db);
|
|
363
|
+
throw std::runtime_error("[op-sqlite] sqlite query error: " +
|
|
364
|
+
std::string(errorMessage));
|
|
365
|
+
}
|
|
354
366
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
367
|
+
// The statement did not fail to parse but there is nothing to do, just
|
|
368
|
+
// skip to the end
|
|
369
|
+
if (statement == nullptr) {
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
358
372
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
double double_value;
|
|
363
|
-
const char *string_value;
|
|
373
|
+
if (params != nullptr && !params->empty()) {
|
|
374
|
+
opsqlite_bind_statement(statement, params);
|
|
375
|
+
}
|
|
364
376
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
377
|
+
column_count = sqlite3_column_count(statement);
|
|
378
|
+
column_names.reserve(column_count);
|
|
379
|
+
bool is_consuming_rows = true;
|
|
380
|
+
double double_value;
|
|
381
|
+
const char *string_value;
|
|
382
|
+
|
|
383
|
+
// Do a first pass to get the column names
|
|
384
|
+
for (int i = 0; i < column_count; i++) {
|
|
385
|
+
column_name = sqlite3_column_name(statement, i);
|
|
386
|
+
column_names.emplace_back(column_name);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
while (is_consuming_rows) {
|
|
390
|
+
status = sqlite3_step(statement);
|
|
370
391
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
int blob_size =
|
|
408
|
-
sqlite3_column_bytes(statement, current_column);
|
|
409
|
-
const void *blob =
|
|
410
|
-
sqlite3_column_blob(statement, current_column);
|
|
411
|
-
auto *data = new uint8_t[blob_size];
|
|
412
|
-
memcpy(data, blob, blob_size);
|
|
413
|
-
row.emplace_back(ArrayBuffer{
|
|
414
|
-
.data = std::shared_ptr<uint8_t>{data},
|
|
392
|
+
switch (status) {
|
|
393
|
+
case SQLITE_ROW:
|
|
394
|
+
current_column = 0;
|
|
395
|
+
row = std::vector<JSVariant>();
|
|
396
|
+
row.reserve(column_count);
|
|
397
|
+
|
|
398
|
+
while (current_column < column_count) {
|
|
399
|
+
column_type = sqlite3_column_type(statement, current_column);
|
|
400
|
+
|
|
401
|
+
switch (column_type) {
|
|
402
|
+
|
|
403
|
+
case SQLITE_INTEGER:
|
|
404
|
+
// intentional fallthrough
|
|
405
|
+
case SQLITE_FLOAT: {
|
|
406
|
+
double_value = sqlite3_column_double(statement, current_column);
|
|
407
|
+
row.emplace_back(double_value);
|
|
408
|
+
break;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
case SQLITE_TEXT: {
|
|
412
|
+
string_value = reinterpret_cast<const char *>(
|
|
413
|
+
sqlite3_column_text(statement, current_column));
|
|
414
|
+
int len = sqlite3_column_bytes(statement, current_column);
|
|
415
|
+
// Specify length too; in case string contains NULL in
|
|
416
|
+
// the middle
|
|
417
|
+
row.emplace_back(std::string(string_value, len));
|
|
418
|
+
break;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
case SQLITE_BLOB: {
|
|
422
|
+
int blob_size = sqlite3_column_bytes(statement, current_column);
|
|
423
|
+
const void *blob = sqlite3_column_blob(statement, current_column);
|
|
424
|
+
auto *data = new uint8_t[blob_size];
|
|
425
|
+
memcpy(data, blob, blob_size);
|
|
426
|
+
row.emplace_back(
|
|
427
|
+
ArrayBuffer{.data = std::shared_ptr<uint8_t>{data},
|
|
415
428
|
.size = static_cast<size_t>(blob_size)});
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
rows.emplace_back(std::move(row));
|
|
430
|
-
break;
|
|
431
|
-
|
|
432
|
-
case SQLITE_DONE:
|
|
433
|
-
is_consuming_rows = false;
|
|
434
|
-
break;
|
|
435
|
-
|
|
436
|
-
default:
|
|
437
|
-
has_failed = true;
|
|
438
|
-
is_consuming_rows = false;
|
|
439
|
-
}
|
|
429
|
+
break;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
case SQLITE_NULL:
|
|
433
|
+
// Intentionally left blank to switch to default case
|
|
434
|
+
default:
|
|
435
|
+
row.emplace_back(nullptr);
|
|
436
|
+
break;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
current_column++;
|
|
440
440
|
}
|
|
441
441
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
442
|
+
rows.emplace_back(std::move(row));
|
|
443
|
+
break;
|
|
444
|
+
|
|
445
|
+
case SQLITE_DONE:
|
|
446
|
+
is_consuming_rows = false;
|
|
447
|
+
break;
|
|
445
448
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
449
|
+
default:
|
|
450
|
+
has_failed = true;
|
|
451
|
+
is_consuming_rows = false;
|
|
452
|
+
}
|
|
450
453
|
}
|
|
451
454
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
455
|
+
sqlite3_finalize(statement);
|
|
456
|
+
} while (remainingStatement != nullptr &&
|
|
457
|
+
strcmp(remainingStatement, "") != 0 && !has_failed);
|
|
458
|
+
|
|
459
|
+
if (has_failed) {
|
|
460
|
+
const char *message = sqlite3_errmsg(db);
|
|
461
|
+
throw std::runtime_error("[op-sqlite] statement execution error: " +
|
|
462
|
+
std::string(message));
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
int changedRowCount = sqlite3_changes(db);
|
|
466
|
+
long long latestInsertRowId = sqlite3_last_insert_rowid(db);
|
|
467
|
+
return {.affectedRows = changedRowCount,
|
|
468
|
+
.insertId = static_cast<double>(latestInsertRowId),
|
|
469
|
+
.rows = std::move(rows),
|
|
470
|
+
.column_names = std::move(column_names)};
|
|
458
471
|
}
|
|
459
472
|
|
|
460
473
|
BridgeResult opsqlite_execute_host_objects(
|
|
@@ -462,162 +475,157 @@ BridgeResult opsqlite_execute_host_objects(
|
|
|
462
475
|
std::vector<DumbHostObject> *results,
|
|
463
476
|
std::shared_ptr<std::vector<SmartHostObject>> &metadatas) {
|
|
464
477
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
478
|
+
sqlite3_stmt *statement;
|
|
479
|
+
const char *errorMessage;
|
|
480
|
+
const char *remainingStatement = nullptr;
|
|
481
|
+
|
|
482
|
+
bool isConsuming = true;
|
|
483
|
+
bool isFailed = false;
|
|
468
484
|
|
|
469
|
-
|
|
470
|
-
bool isFailed = false;
|
|
485
|
+
int result = SQLITE_OK;
|
|
471
486
|
|
|
472
|
-
|
|
487
|
+
do {
|
|
488
|
+
const char *queryStr =
|
|
489
|
+
remainingStatement == nullptr ? query.c_str() : remainingStatement;
|
|
473
490
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
491
|
+
int statementStatus =
|
|
492
|
+
sqlite3_prepare_v2(db, queryStr, -1, &statement, &remainingStatement);
|
|
493
|
+
|
|
494
|
+
if (statementStatus != SQLITE_OK) {
|
|
495
|
+
const char *message = sqlite3_errmsg(db);
|
|
496
|
+
throw std::runtime_error(
|
|
497
|
+
"[op-sqlite] SQL statement error on opsqlite_execute:\n" +
|
|
498
|
+
std::to_string(statementStatus) + " description:\n" +
|
|
499
|
+
std::string(message));
|
|
500
|
+
}
|
|
477
501
|
|
|
478
|
-
|
|
479
|
-
|
|
502
|
+
// The statement did not fail to parse but there is nothing to do, just
|
|
503
|
+
// skip to the end
|
|
504
|
+
if (statement == nullptr) {
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
480
507
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
"[op-sqlite] SQL statement error on opsqlite_execute:\n" +
|
|
485
|
-
std::to_string(statementStatus) + " description:\n" +
|
|
486
|
-
std::string(message));
|
|
487
|
-
}
|
|
508
|
+
if (params != nullptr && !params->empty()) {
|
|
509
|
+
opsqlite_bind_statement(statement, params);
|
|
510
|
+
}
|
|
488
511
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
if (statement == nullptr) {
|
|
492
|
-
continue;
|
|
493
|
-
}
|
|
512
|
+
int i, count, column_type;
|
|
513
|
+
std::string column_name, column_declared_type;
|
|
494
514
|
|
|
495
|
-
|
|
496
|
-
|
|
515
|
+
while (isConsuming) {
|
|
516
|
+
result = sqlite3_step(statement);
|
|
517
|
+
|
|
518
|
+
switch (result) {
|
|
519
|
+
case SQLITE_ROW: {
|
|
520
|
+
if (results == nullptr) {
|
|
521
|
+
break;
|
|
497
522
|
}
|
|
498
523
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
// Specify length too; in case string contains NULL in
|
|
544
|
-
// the middle
|
|
545
|
-
row.values.emplace_back(
|
|
546
|
-
std::string(column_value, byteLen));
|
|
547
|
-
break;
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
case SQLITE_BLOB: {
|
|
551
|
-
int blob_size = sqlite3_column_bytes(statement, i);
|
|
552
|
-
const void *blob = sqlite3_column_blob(statement, i);
|
|
553
|
-
auto *data = new uint8_t[blob_size];
|
|
554
|
-
// You cannot share raw memory between native and JS
|
|
555
|
-
// always copy the data
|
|
556
|
-
memcpy(data, blob, blob_size);
|
|
557
|
-
row.values.emplace_back(ArrayBuffer{
|
|
558
|
-
.data = std::shared_ptr<uint8_t>{data},
|
|
524
|
+
i = 0;
|
|
525
|
+
DumbHostObject row = DumbHostObject(metadatas);
|
|
526
|
+
|
|
527
|
+
count = sqlite3_column_count(statement);
|
|
528
|
+
|
|
529
|
+
while (i < count) {
|
|
530
|
+
column_type = sqlite3_column_type(statement, i);
|
|
531
|
+
|
|
532
|
+
switch (column_type) {
|
|
533
|
+
case SQLITE_INTEGER: {
|
|
534
|
+
/**
|
|
535
|
+
* Warning this will loose precision because JS can
|
|
536
|
+
* only represent Integers up to 53 bits
|
|
537
|
+
*/
|
|
538
|
+
double column_value = sqlite3_column_double(statement, i);
|
|
539
|
+
row.values.emplace_back(column_value);
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
case SQLITE_FLOAT: {
|
|
544
|
+
double column_value = sqlite3_column_double(statement, i);
|
|
545
|
+
row.values.emplace_back(column_value);
|
|
546
|
+
break;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
case SQLITE_TEXT: {
|
|
550
|
+
const char *column_value = reinterpret_cast<const char *>(
|
|
551
|
+
sqlite3_column_text(statement, i));
|
|
552
|
+
int byteLen = sqlite3_column_bytes(statement, i);
|
|
553
|
+
// Specify length too; in case string contains NULL in
|
|
554
|
+
// the middle
|
|
555
|
+
row.values.emplace_back(std::string(column_value, byteLen));
|
|
556
|
+
break;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
case SQLITE_BLOB: {
|
|
560
|
+
int blob_size = sqlite3_column_bytes(statement, i);
|
|
561
|
+
const void *blob = sqlite3_column_blob(statement, i);
|
|
562
|
+
auto *data = new uint8_t[blob_size];
|
|
563
|
+
// You cannot share raw memory between native and JS
|
|
564
|
+
// always copy the data
|
|
565
|
+
memcpy(data, blob, blob_size);
|
|
566
|
+
row.values.emplace_back(
|
|
567
|
+
ArrayBuffer{.data = std::shared_ptr<uint8_t>{data},
|
|
559
568
|
.size = static_cast<size_t>(blob_size)});
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
results->emplace_back(row);
|
|
574
|
-
break;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
case SQLITE_DONE:
|
|
578
|
-
if (metadatas != nullptr) {
|
|
579
|
-
i = 0;
|
|
580
|
-
count = sqlite3_column_count(statement);
|
|
581
|
-
|
|
582
|
-
while (i < count) {
|
|
583
|
-
column_name = sqlite3_column_name(statement, i);
|
|
584
|
-
const char *type =
|
|
585
|
-
sqlite3_column_decltype(statement, i);
|
|
586
|
-
auto metadata = SmartHostObject();
|
|
587
|
-
metadata.fields.emplace_back("name", column_name);
|
|
588
|
-
metadata.fields.emplace_back("index", i);
|
|
589
|
-
metadata.fields.emplace_back(
|
|
590
|
-
"type", type == nullptr ? "UNKNOWN" : type);
|
|
591
|
-
|
|
592
|
-
metadatas->push_back(metadata);
|
|
593
|
-
i++;
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
isConsuming = false;
|
|
597
|
-
break;
|
|
598
|
-
|
|
599
|
-
default:
|
|
600
|
-
errorMessage = sqlite3_errmsg(db);
|
|
601
|
-
isFailed = true;
|
|
602
|
-
isConsuming = false;
|
|
603
|
-
}
|
|
569
|
+
break;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
case SQLITE_NULL:
|
|
573
|
+
// Intentionally left blank
|
|
574
|
+
|
|
575
|
+
default:
|
|
576
|
+
row.values.emplace_back(nullptr);
|
|
577
|
+
break;
|
|
578
|
+
}
|
|
579
|
+
i++;
|
|
604
580
|
}
|
|
605
581
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
582
|
+
results->emplace_back(row);
|
|
583
|
+
break;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
case SQLITE_DONE:
|
|
587
|
+
if (metadatas != nullptr) {
|
|
588
|
+
i = 0;
|
|
589
|
+
count = sqlite3_column_count(statement);
|
|
609
590
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
591
|
+
while (i < count) {
|
|
592
|
+
column_name = sqlite3_column_name(statement, i);
|
|
593
|
+
const char *type = sqlite3_column_decltype(statement, i);
|
|
594
|
+
auto metadata = SmartHostObject();
|
|
595
|
+
metadata.fields.emplace_back("name", column_name);
|
|
596
|
+
metadata.fields.emplace_back("index", i);
|
|
597
|
+
metadata.fields.emplace_back("type",
|
|
598
|
+
type == nullptr ? "UNKNOWN" : type);
|
|
599
|
+
|
|
600
|
+
metadatas->push_back(metadata);
|
|
601
|
+
i++;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
isConsuming = false;
|
|
605
|
+
break;
|
|
606
|
+
|
|
607
|
+
default:
|
|
608
|
+
errorMessage = sqlite3_errmsg(db);
|
|
609
|
+
isFailed = true;
|
|
610
|
+
isConsuming = false;
|
|
611
|
+
}
|
|
614
612
|
}
|
|
615
613
|
|
|
616
|
-
|
|
617
|
-
|
|
614
|
+
sqlite3_finalize(statement);
|
|
615
|
+
} while (remainingStatement != nullptr &&
|
|
616
|
+
strcmp(remainingStatement, "") != 0 && !isFailed);
|
|
618
617
|
|
|
619
|
-
|
|
620
|
-
|
|
618
|
+
if (isFailed) {
|
|
619
|
+
throw std::runtime_error(
|
|
620
|
+
"[op-sqlite] SQLite error code: " + std::to_string(result) +
|
|
621
|
+
", description: " + std::string(errorMessage));
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
int changedRowCount = sqlite3_changes(db);
|
|
625
|
+
long long latestInsertRowId = sqlite3_last_insert_rowid(db);
|
|
626
|
+
|
|
627
|
+
return {.affectedRows = changedRowCount,
|
|
628
|
+
.insertId = static_cast<double>(latestInsertRowId)};
|
|
621
629
|
}
|
|
622
630
|
|
|
623
631
|
/// Executes returning data in raw arrays, a small performance optimization
|
|
@@ -626,245 +634,243 @@ BridgeResult
|
|
|
626
634
|
opsqlite_execute_raw(sqlite3 *db, std::string const &query,
|
|
627
635
|
const std::vector<JSVariant> *params,
|
|
628
636
|
std::vector<std::vector<JSVariant>> *results) {
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
637
|
+
sqlite3_stmt *statement;
|
|
638
|
+
const char *errorMessage;
|
|
639
|
+
const char *remainingStatement = nullptr;
|
|
632
640
|
|
|
633
|
-
|
|
634
|
-
|
|
641
|
+
bool isConsuming = true;
|
|
642
|
+
bool isFailed = false;
|
|
635
643
|
|
|
636
|
-
|
|
644
|
+
int step = SQLITE_OK;
|
|
637
645
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
646
|
+
do {
|
|
647
|
+
const char *queryStr =
|
|
648
|
+
remainingStatement == nullptr ? query.c_str() : remainingStatement;
|
|
641
649
|
|
|
642
|
-
|
|
643
|
-
|
|
650
|
+
int statementStatus =
|
|
651
|
+
sqlite3_prepare_v2(db, queryStr, -1, &statement, &remainingStatement);
|
|
644
652
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
653
|
+
if (statementStatus != SQLITE_OK) {
|
|
654
|
+
const char *message = sqlite3_errmsg(db);
|
|
655
|
+
throw std::runtime_error(
|
|
656
|
+
"[op-sqlite] SQL statement error:" + std::to_string(statementStatus) +
|
|
657
|
+
" description:" + std::string(message));
|
|
658
|
+
}
|
|
651
659
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
660
|
+
// The statement did not fail to parse but there is nothing to do, just
|
|
661
|
+
// skip to the end
|
|
662
|
+
if (statement == nullptr) {
|
|
663
|
+
continue;
|
|
664
|
+
}
|
|
657
665
|
|
|
658
|
-
|
|
659
|
-
|
|
666
|
+
if (params != nullptr && !params->empty()) {
|
|
667
|
+
opsqlite_bind_statement(statement, params);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
int i, column_type;
|
|
671
|
+
std::string column_name, column_declared_type;
|
|
672
|
+
|
|
673
|
+
int column_count = sqlite3_column_count(statement);
|
|
674
|
+
|
|
675
|
+
while (isConsuming) {
|
|
676
|
+
step = sqlite3_step(statement);
|
|
677
|
+
|
|
678
|
+
switch (step) {
|
|
679
|
+
case SQLITE_ROW: {
|
|
680
|
+
if (results == nullptr) {
|
|
681
|
+
break;
|
|
660
682
|
}
|
|
661
683
|
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
while (
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
reinterpret_cast<const char *>(
|
|
696
|
-
sqlite3_column_text(statement, i));
|
|
697
|
-
int byteLen = sqlite3_column_bytes(statement, i);
|
|
698
|
-
// Specify length too; in case string contains NULL in
|
|
699
|
-
// the middle
|
|
700
|
-
row.emplace_back(std::string(column_value, byteLen));
|
|
701
|
-
break;
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
case SQLITE_BLOB: {
|
|
705
|
-
int blob_size = sqlite3_column_bytes(statement, i);
|
|
706
|
-
const void *blob = sqlite3_column_blob(statement, i);
|
|
707
|
-
auto *data = new uint8_t[blob_size];
|
|
708
|
-
memcpy(data, blob, blob_size);
|
|
709
|
-
row.emplace_back(ArrayBuffer{
|
|
710
|
-
.data = std::shared_ptr<uint8_t>{data},
|
|
684
|
+
std::vector<JSVariant> row;
|
|
685
|
+
row.reserve(column_count);
|
|
686
|
+
|
|
687
|
+
i = 0;
|
|
688
|
+
|
|
689
|
+
while (i < column_count) {
|
|
690
|
+
column_type = sqlite3_column_type(statement, i);
|
|
691
|
+
|
|
692
|
+
switch (column_type) {
|
|
693
|
+
case SQLITE_INTEGER:
|
|
694
|
+
case SQLITE_FLOAT: {
|
|
695
|
+
double column_value = sqlite3_column_double(statement, i);
|
|
696
|
+
row.emplace_back(column_value);
|
|
697
|
+
break;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
case SQLITE_TEXT: {
|
|
701
|
+
const char *column_value = reinterpret_cast<const char *>(
|
|
702
|
+
sqlite3_column_text(statement, i));
|
|
703
|
+
int byteLen = sqlite3_column_bytes(statement, i);
|
|
704
|
+
// Specify length too; in case string contains NULL in
|
|
705
|
+
// the middle
|
|
706
|
+
row.emplace_back(std::string(column_value, byteLen));
|
|
707
|
+
break;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
case SQLITE_BLOB: {
|
|
711
|
+
int blob_size = sqlite3_column_bytes(statement, i);
|
|
712
|
+
const void *blob = sqlite3_column_blob(statement, i);
|
|
713
|
+
auto *data = new uint8_t[blob_size];
|
|
714
|
+
memcpy(data, blob, blob_size);
|
|
715
|
+
row.emplace_back(
|
|
716
|
+
ArrayBuffer{.data = std::shared_ptr<uint8_t>{data},
|
|
711
717
|
.size = static_cast<size_t>(blob_size)});
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
results->emplace_back(row);
|
|
725
|
-
|
|
726
|
-
break;
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
case SQLITE_DONE:
|
|
730
|
-
isConsuming = false;
|
|
731
|
-
break;
|
|
732
|
-
|
|
733
|
-
default:
|
|
734
|
-
errorMessage = sqlite3_errmsg(db);
|
|
735
|
-
isFailed = true;
|
|
736
|
-
isConsuming = false;
|
|
737
|
-
}
|
|
718
|
+
break;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
case SQLITE_NULL:
|
|
722
|
+
// intentional fallthrough
|
|
723
|
+
default:
|
|
724
|
+
row.emplace_back(nullptr);
|
|
725
|
+
break;
|
|
726
|
+
}
|
|
727
|
+
i++;
|
|
738
728
|
}
|
|
739
729
|
|
|
740
|
-
|
|
741
|
-
} while (remainingStatement != nullptr &&
|
|
742
|
-
strcmp(remainingStatement, "") != 0 && !isFailed);
|
|
730
|
+
results->emplace_back(row);
|
|
743
731
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
732
|
+
break;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
case SQLITE_DONE:
|
|
736
|
+
isConsuming = false;
|
|
737
|
+
break;
|
|
738
|
+
|
|
739
|
+
default:
|
|
740
|
+
errorMessage = sqlite3_errmsg(db);
|
|
741
|
+
isFailed = true;
|
|
742
|
+
isConsuming = false;
|
|
743
|
+
}
|
|
748
744
|
}
|
|
749
745
|
|
|
750
|
-
|
|
751
|
-
|
|
746
|
+
sqlite3_finalize(statement);
|
|
747
|
+
} while (remainingStatement != nullptr &&
|
|
748
|
+
strcmp(remainingStatement, "") != 0 && !isFailed);
|
|
749
|
+
|
|
750
|
+
if (isFailed) {
|
|
751
|
+
throw std::runtime_error(
|
|
752
|
+
"[op-sqlite] SQLite error code: " + std::to_string(step) +
|
|
753
|
+
", description: " + std::string(errorMessage));
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
int changedRowCount = sqlite3_changes(db);
|
|
757
|
+
long long latestInsertRowId = sqlite3_last_insert_rowid(db);
|
|
752
758
|
|
|
753
|
-
|
|
754
|
-
|
|
759
|
+
return {.affectedRows = changedRowCount,
|
|
760
|
+
.insertId = static_cast<double>(latestInsertRowId)};
|
|
755
761
|
}
|
|
756
762
|
|
|
757
763
|
std::string operation_to_string(int operation_type) {
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
764
|
+
switch (operation_type) {
|
|
765
|
+
case SQLITE_INSERT:
|
|
766
|
+
return "INSERT";
|
|
761
767
|
|
|
762
|
-
|
|
763
|
-
|
|
768
|
+
case SQLITE_DELETE:
|
|
769
|
+
return "DELETE";
|
|
764
770
|
|
|
765
|
-
|
|
766
|
-
|
|
771
|
+
case SQLITE_UPDATE:
|
|
772
|
+
return "UPDATE";
|
|
767
773
|
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
774
|
+
default:
|
|
775
|
+
throw std::runtime_error("Unknown SQLite operation on hook");
|
|
776
|
+
}
|
|
771
777
|
}
|
|
772
778
|
|
|
773
779
|
void update_callback(void *db_host_object_ptr, int operation_type,
|
|
774
780
|
[[maybe_unused]] char const *database, char const *table,
|
|
775
781
|
sqlite3_int64 row_id) {
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
782
|
+
auto db_host_object = reinterpret_cast<DBHostObject *>(db_host_object_ptr);
|
|
783
|
+
db_host_object->on_update(std::string(table),
|
|
784
|
+
operation_to_string(operation_type), row_id);
|
|
779
785
|
}
|
|
780
786
|
|
|
781
787
|
void opsqlite_register_update_hook(sqlite3 *db, void *db_host_object) {
|
|
782
|
-
|
|
788
|
+
sqlite3_update_hook(db, &update_callback, (void *)db_host_object);
|
|
783
789
|
}
|
|
784
790
|
|
|
785
791
|
void opsqlite_deregister_update_hook(sqlite3 *db) {
|
|
786
|
-
|
|
792
|
+
sqlite3_update_hook(db, nullptr, nullptr);
|
|
787
793
|
}
|
|
788
794
|
|
|
789
795
|
int commit_callback(void *db_host_object_ptr) {
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
796
|
+
auto db_host_object = reinterpret_cast<DBHostObject *>(db_host_object_ptr);
|
|
797
|
+
db_host_object->on_commit();
|
|
798
|
+
return 0;
|
|
793
799
|
}
|
|
794
800
|
|
|
795
801
|
void opsqlite_register_commit_hook(sqlite3 *db, void *db_host_object_ptr) {
|
|
796
|
-
|
|
802
|
+
sqlite3_commit_hook(db, &commit_callback, db_host_object_ptr);
|
|
797
803
|
}
|
|
798
804
|
|
|
799
805
|
void opsqlite_deregister_commit_hook(sqlite3 *db) {
|
|
800
|
-
|
|
806
|
+
sqlite3_commit_hook(db, nullptr, nullptr);
|
|
801
807
|
}
|
|
802
808
|
|
|
803
809
|
void rollback_callback(void *db_host_object_ptr) {
|
|
804
|
-
|
|
805
|
-
|
|
810
|
+
auto db_host_object = reinterpret_cast<DBHostObject *>(db_host_object_ptr);
|
|
811
|
+
db_host_object->on_rollback();
|
|
806
812
|
}
|
|
807
813
|
|
|
808
814
|
void opsqlite_register_rollback_hook(sqlite3 *db, void *db_host_object_ptr) {
|
|
809
|
-
|
|
815
|
+
sqlite3_rollback_hook(db, &rollback_callback, db_host_object_ptr);
|
|
810
816
|
}
|
|
811
817
|
|
|
812
818
|
void opsqlite_deregister_rollback_hook(sqlite3 *db) {
|
|
813
|
-
|
|
819
|
+
sqlite3_rollback_hook(db, nullptr, nullptr);
|
|
814
820
|
}
|
|
815
821
|
|
|
816
822
|
void opsqlite_load_extension(sqlite3 *db, std::string &path,
|
|
817
823
|
std::string &entry_point) {
|
|
818
824
|
#ifdef OP_SQLITE_USE_PHONE_VERSION
|
|
819
|
-
|
|
820
|
-
|
|
825
|
+
throw std::runtime_error("[op-sqlite] Embedded version of SQLite does not "
|
|
826
|
+
"support loading extensions");
|
|
821
827
|
#else
|
|
822
|
-
|
|
823
|
-
|
|
828
|
+
int status = 0;
|
|
829
|
+
status = sqlite3_enable_load_extension(db, 1);
|
|
824
830
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
831
|
+
if (status != SQLITE_OK) {
|
|
832
|
+
throw std::runtime_error("Could not enable extension loading");
|
|
833
|
+
}
|
|
828
834
|
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
835
|
+
const char *entry_point_cstr = nullptr;
|
|
836
|
+
if (!entry_point.empty()) {
|
|
837
|
+
entry_point_cstr = entry_point.c_str();
|
|
838
|
+
}
|
|
833
839
|
|
|
834
|
-
|
|
840
|
+
char *error_message;
|
|
835
841
|
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
842
|
+
status = sqlite3_load_extension(db, path.c_str(), entry_point_cstr,
|
|
843
|
+
&error_message);
|
|
844
|
+
if (status != SQLITE_OK) {
|
|
845
|
+
throw std::runtime_error(error_message);
|
|
846
|
+
}
|
|
841
847
|
#endif
|
|
842
848
|
}
|
|
843
849
|
|
|
844
850
|
BatchResult
|
|
845
851
|
opsqlite_execute_batch(sqlite3 *db,
|
|
846
852
|
const std::vector<BatchArguments> *commands) {
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
853
|
+
size_t commandCount = commands->size();
|
|
854
|
+
if (commandCount <= 0) {
|
|
855
|
+
throw std::runtime_error("No SQL commands provided");
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
int affectedRows = 0;
|
|
859
|
+
// opsqlite_execute(db, "BEGIN EXCLUSIVE TRANSACTION", nullptr);
|
|
860
|
+
for (int i = 0; i < commandCount; i++) {
|
|
861
|
+
const auto &command = commands->at(i);
|
|
862
|
+
// We do not provide a datastructure to receive query data because we
|
|
863
|
+
// don't need/want to handle this results in a batch execution
|
|
864
|
+
// There is also no need to commit/catch this transaction, this is done
|
|
865
|
+
// in the JS code
|
|
866
|
+
auto result = opsqlite_execute(db, command.sql, &command.params);
|
|
867
|
+
affectedRows += result.affectedRows;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
return BatchResult{
|
|
871
|
+
.affectedRows = affectedRows,
|
|
872
|
+
.commands = static_cast<int>(commandCount),
|
|
873
|
+
};
|
|
868
874
|
}
|
|
869
875
|
|
|
870
876
|
} // namespace opsqlite
|