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