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