@op-engineering/op-sqlite 6.0.2-beta1 → 6.0.2-beta3
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 +26 -15
- package/android/build.gradle +10 -1
- package/android/cpp-adapter.cpp +4 -0
- 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/cpp/DBHostObject.cpp +208 -81
- package/cpp/DBHostObject.h +10 -5
- package/cpp/PreparedStatementHostObject.cpp +28 -14
- package/cpp/PreparedStatementHostObject.h +18 -14
- package/cpp/bindings.cpp +14 -12
- package/cpp/bindings.h +2 -0
- package/cpp/bridge.cpp +45 -0
- package/cpp/bridge.h +3 -0
- package/cpp/libsql/bridge.cpp +629 -0
- package/cpp/libsql/bridge.h +88 -0
- package/cpp/libsql/libsql.h +133 -0
- package/cpp/sqlite3.h +0 -1
- package/cpp/types.h +5 -0
- package/cpp/utils.cpp +45 -3
- package/cpp/utils.h +2 -3
- package/ios/libsql.xcframework/Info.plist +48 -0
- package/ios/libsql.xcframework/ios-arm64/Headers/libsql.h +133 -0
- package/ios/libsql.xcframework/ios-arm64/libsql_experimental.a +0 -0
- package/ios/libsql.xcframework/ios-arm64_x86_64-simulator/Headers/libsql.h +133 -0
- package/ios/libsql.xcframework/ios-arm64_x86_64-simulator/libsql_experimental.a +0 -0
- package/lib/commonjs/index.js +5 -2
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/index.js +3 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/index.d.ts +6 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/op-sqlite.podspec +20 -3
- package/package.json +1 -1
- package/src/index.ts +10 -3
- package/cpp/sqlbatchexecutor.cpp +0 -93
- package/cpp/sqlbatchexecutor.h +0 -30
|
@@ -0,0 +1,629 @@
|
|
|
1
|
+
#include "bridge.h"
|
|
2
|
+
#include "DumbHostObject.h"
|
|
3
|
+
#include "SmartHostObject.h"
|
|
4
|
+
#include "logs.h"
|
|
5
|
+
#include "utils.h"
|
|
6
|
+
#include <iostream>
|
|
7
|
+
#include <unordered_map>
|
|
8
|
+
#include <variant>
|
|
9
|
+
|
|
10
|
+
namespace opsqlite {
|
|
11
|
+
|
|
12
|
+
struct DB {
|
|
13
|
+
libsql_database_t db;
|
|
14
|
+
libsql_connection_t c;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
std::unordered_map<std::string, DB> db_map =
|
|
18
|
+
std::unordered_map<std::string, DB>();
|
|
19
|
+
|
|
20
|
+
inline void check_db_open(std::string const &db_name) {
|
|
21
|
+
if (db_map.count(db_name) == 0) {
|
|
22
|
+
throw std::runtime_error("[OP-SQLite] DB is not open");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// _____ _____
|
|
26
|
+
// /\ | __ \_ _|
|
|
27
|
+
// / \ | |__) || |
|
|
28
|
+
// / /\ \ | ___/ | |
|
|
29
|
+
// / ____ \| | _| |_
|
|
30
|
+
// /_/ \_\_| |_____|
|
|
31
|
+
|
|
32
|
+
/// Returns the completely formed db path, but it also creates any sub-folders
|
|
33
|
+
/// along the way
|
|
34
|
+
std::string opsqlite_get_db_path(std::string const &db_name,
|
|
35
|
+
std::string const &location) {
|
|
36
|
+
|
|
37
|
+
if (location == ":memory:") {
|
|
38
|
+
return location;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
mkdir(location);
|
|
42
|
+
return location + "/" + db_name;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
#ifdef OP_SQLITE_USE_SQLCIPHER
|
|
46
|
+
BridgeResult opsqlite_open(std::string const &dbName,
|
|
47
|
+
std::string const &last_path,
|
|
48
|
+
std::string const &crsqlitePath,
|
|
49
|
+
std::string const &encryptionKey) {
|
|
50
|
+
#else
|
|
51
|
+
BridgeResult opsqlite_libsql_open(std::string const &name,
|
|
52
|
+
std::string const &last_path) {
|
|
53
|
+
#endif
|
|
54
|
+
std::string path = opsqlite_get_db_path(name, last_path);
|
|
55
|
+
|
|
56
|
+
int status = 0;
|
|
57
|
+
libsql_database_t db;
|
|
58
|
+
libsql_connection_t c;
|
|
59
|
+
const char *err = NULL;
|
|
60
|
+
|
|
61
|
+
status = libsql_open_file(path.c_str(), &db, &err);
|
|
62
|
+
|
|
63
|
+
if (status != 0) {
|
|
64
|
+
return {.type = SQLiteError, .message = err};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
status = libsql_connect(db, &c, &err);
|
|
68
|
+
|
|
69
|
+
if (status != 0) {
|
|
70
|
+
return {.type = SQLiteError, .message = err};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
db_map[name] = {.db = db, .c = c};
|
|
74
|
+
|
|
75
|
+
#ifdef OP_SQLITE_USE_SQLCIPHER
|
|
76
|
+
opsqlite_execute(dbName, "PRAGMA key = '" + encryptionKey + "'", nullptr,
|
|
77
|
+
nullptr, nullptr);
|
|
78
|
+
#endif
|
|
79
|
+
|
|
80
|
+
return {.type = SQLiteOk, .affectedRows = 0};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
BridgeResult opsqlite_libsql_close(std::string const &name) {
|
|
84
|
+
|
|
85
|
+
check_db_open(name);
|
|
86
|
+
|
|
87
|
+
DB db = db_map[name];
|
|
88
|
+
|
|
89
|
+
libsql_disconnect(db.c);
|
|
90
|
+
libsql_close(db.db);
|
|
91
|
+
|
|
92
|
+
db_map.erase(name);
|
|
93
|
+
|
|
94
|
+
return BridgeResult{
|
|
95
|
+
.type = SQLiteOk,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
BridgeResult opsqlite_libsql_attach(std::string const &mainDBName,
|
|
100
|
+
std::string const &docPath,
|
|
101
|
+
std::string const &databaseToAttach,
|
|
102
|
+
std::string const &alias) {
|
|
103
|
+
std::string dbPath = opsqlite_get_db_path(databaseToAttach, docPath);
|
|
104
|
+
std::string statement = "ATTACH DATABASE '" + dbPath + "' AS " + alias;
|
|
105
|
+
|
|
106
|
+
BridgeResult result =
|
|
107
|
+
opsqlite_libsql_execute(mainDBName, statement, nullptr, nullptr, nullptr);
|
|
108
|
+
|
|
109
|
+
if (result.type == SQLiteError) {
|
|
110
|
+
return {
|
|
111
|
+
.type = SQLiteError,
|
|
112
|
+
.message = mainDBName + " was unable to attach another database: " +
|
|
113
|
+
std::string(result.message),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
.type = SQLiteOk,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
BridgeResult opsqlite_libsql_detach(std::string const &mainDBName,
|
|
122
|
+
std::string const &alias) {
|
|
123
|
+
std::string statement = "DETACH DATABASE " + alias;
|
|
124
|
+
BridgeResult result =
|
|
125
|
+
opsqlite_libsql_execute(mainDBName, statement, nullptr, nullptr, nullptr);
|
|
126
|
+
if (result.type == SQLiteError) {
|
|
127
|
+
return BridgeResult{
|
|
128
|
+
.type = SQLiteError,
|
|
129
|
+
.message = mainDBName + "was unable to detach database: " +
|
|
130
|
+
std::string(result.message),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return BridgeResult{
|
|
134
|
+
.type = SQLiteOk,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
BridgeResult opsqlite_libsql_remove(std::string const &name,
|
|
139
|
+
std::string const &path) {
|
|
140
|
+
if (db_map.count(name) == 1) {
|
|
141
|
+
BridgeResult closeResult = opsqlite_libsql_close(name);
|
|
142
|
+
if (closeResult.type == SQLiteError) {
|
|
143
|
+
return closeResult;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
std::string full_path = opsqlite_get_db_path(name, path);
|
|
148
|
+
|
|
149
|
+
if (!file_exists(full_path)) {
|
|
150
|
+
return {.type = SQLiteError,
|
|
151
|
+
.message = "[op-sqlite]: Database file not found" + full_path};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
remove(full_path.c_str());
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
.type = SQLiteOk,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
void opsqlite_libsql_bind_statement(libsql_stmt_t statement,
|
|
162
|
+
const std::vector<JSVariant> *values) {
|
|
163
|
+
|
|
164
|
+
// sqlite3_clear_bindings(statement);
|
|
165
|
+
const char *err;
|
|
166
|
+
size_t size = values->size();
|
|
167
|
+
|
|
168
|
+
for (int ii = 0; ii < size; ii++) {
|
|
169
|
+
int index = ii + 1;
|
|
170
|
+
JSVariant value = values->at(ii);
|
|
171
|
+
|
|
172
|
+
if (std::holds_alternative<bool>(value)) {
|
|
173
|
+
libsql_bind_int(statement, index, std::get<int>(value), &err);
|
|
174
|
+
} else if (std::holds_alternative<int>(value)) {
|
|
175
|
+
libsql_bind_int(statement, index, std::get<int>(value), &err);
|
|
176
|
+
} else if (std::holds_alternative<long long>(value)) {
|
|
177
|
+
libsql_bind_int(statement, index, std::get<long long>(value), &err);
|
|
178
|
+
} else if (std::holds_alternative<double>(value)) {
|
|
179
|
+
libsql_bind_float(statement, index, std::get<double>(value), &err);
|
|
180
|
+
} else if (std::holds_alternative<std::string>(value)) {
|
|
181
|
+
std::string str = std::get<std::string>(value);
|
|
182
|
+
libsql_bind_string(statement, index, str.c_str(), &err);
|
|
183
|
+
} else if (std::holds_alternative<ArrayBuffer>(value)) {
|
|
184
|
+
ArrayBuffer buffer = std::get<ArrayBuffer>(value);
|
|
185
|
+
libsql_bind_blob(statement, index, buffer.data.get(), buffer.size, &err);
|
|
186
|
+
} else {
|
|
187
|
+
libsql_bind_null(statement, index, &err);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
BridgeResult opsqlite_libsql_execute_prepared_statement(
|
|
193
|
+
std::string const &name, libsql_stmt_t stmt,
|
|
194
|
+
std::vector<DumbHostObject> *results,
|
|
195
|
+
std::shared_ptr<std::vector<SmartHostObject>> metadatas) {
|
|
196
|
+
|
|
197
|
+
check_db_open(name);
|
|
198
|
+
|
|
199
|
+
libsql_connection_t c = db_map[name].c;
|
|
200
|
+
libsql_rows_t rows;
|
|
201
|
+
libsql_row_t row;
|
|
202
|
+
|
|
203
|
+
int status = 0;
|
|
204
|
+
const char *err = NULL;
|
|
205
|
+
|
|
206
|
+
if (status != 0) {
|
|
207
|
+
return {.type = SQLiteError, .message = err};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
status = libsql_query_stmt(stmt, &rows, &err);
|
|
211
|
+
|
|
212
|
+
if (status != 0) {
|
|
213
|
+
return {.type = SQLiteError, .message = err};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
bool metadata_set = false;
|
|
217
|
+
|
|
218
|
+
int num_cols = libsql_column_count(rows);
|
|
219
|
+
while ((status = libsql_next_row(rows, &row, &err)) == 0) {
|
|
220
|
+
|
|
221
|
+
if (!err && !row) {
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
DumbHostObject row_host_object = DumbHostObject(metadatas);
|
|
226
|
+
|
|
227
|
+
for (int col = 0; col < num_cols; col++) {
|
|
228
|
+
int type;
|
|
229
|
+
|
|
230
|
+
libsql_column_type(rows, row, col, &type, &err);
|
|
231
|
+
|
|
232
|
+
switch (type) {
|
|
233
|
+
case LIBSQL_INT:
|
|
234
|
+
long long int_value;
|
|
235
|
+
status = libsql_get_int(row, col, &int_value, &err);
|
|
236
|
+
row_host_object.values.push_back(JSVariant(int_value));
|
|
237
|
+
break;
|
|
238
|
+
|
|
239
|
+
case LIBSQL_FLOAT:
|
|
240
|
+
double float_value;
|
|
241
|
+
status = libsql_get_float(row, col, &float_value, &err);
|
|
242
|
+
row_host_object.values.push_back(JSVariant(float_value));
|
|
243
|
+
break;
|
|
244
|
+
|
|
245
|
+
case LIBSQL_TEXT:
|
|
246
|
+
const char *text_value;
|
|
247
|
+
status = libsql_get_string(row, col, &text_value, &err);
|
|
248
|
+
row_host_object.values.push_back(JSVariant(text_value));
|
|
249
|
+
break;
|
|
250
|
+
|
|
251
|
+
case LIBSQL_BLOB: {
|
|
252
|
+
blob value_blob;
|
|
253
|
+
libsql_get_blob(row, col, &value_blob, &err);
|
|
254
|
+
uint8_t *data = new uint8_t[value_blob.len];
|
|
255
|
+
// You cannot share raw memory between native and JS
|
|
256
|
+
// always copy the data
|
|
257
|
+
memcpy(data, value_blob.ptr, value_blob.len);
|
|
258
|
+
libsql_free_blob(value_blob);
|
|
259
|
+
row_host_object.values.push_back(JSVariant(
|
|
260
|
+
ArrayBuffer{.data = std::shared_ptr<uint8_t>{data},
|
|
261
|
+
.size = static_cast<size_t>(value_blob.len)}));
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
case LIBSQL_NULL:
|
|
266
|
+
// intentional fall-through
|
|
267
|
+
default:
|
|
268
|
+
row_host_object.values.push_back(JSVariant(nullptr));
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (status != 0) {
|
|
273
|
+
fprintf(stderr, "%s\n", err);
|
|
274
|
+
throw std::runtime_error("libsql error");
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// On the first interation through the columns, set the metadata
|
|
278
|
+
if (!metadata_set && metadatas != nullptr) {
|
|
279
|
+
const char *col_name;
|
|
280
|
+
status = libsql_column_name(rows, col, &col_name, &err);
|
|
281
|
+
|
|
282
|
+
auto metadata = SmartHostObject();
|
|
283
|
+
metadata.fields.push_back(std::make_pair("name", col_name));
|
|
284
|
+
metadata.fields.push_back(std::make_pair("index", col));
|
|
285
|
+
metadata.fields.push_back(std::make_pair("type", "UNKNOWN"));
|
|
286
|
+
// metadata.fields.push_back(
|
|
287
|
+
// std::make_pair("type", type == -1 ? "UNKNOWN" :
|
|
288
|
+
// type));
|
|
289
|
+
|
|
290
|
+
metadatas->push_back(metadata);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (results != nullptr) {
|
|
295
|
+
results->push_back(row_host_object);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
metadata_set = true;
|
|
299
|
+
err = NULL;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (status != 0) {
|
|
303
|
+
fprintf(stderr, "%s\n", err);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
libsql_free_rows(rows);
|
|
307
|
+
|
|
308
|
+
int changes = libsql_changes(c);
|
|
309
|
+
long long insert_row_id = libsql_last_insert_rowid(c);
|
|
310
|
+
|
|
311
|
+
libsql_reset_stmt(stmt, &err);
|
|
312
|
+
|
|
313
|
+
return {.type = SQLiteOk,
|
|
314
|
+
.affectedRows = changes,
|
|
315
|
+
.insertId = static_cast<double>(insert_row_id)};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
libsql_stmt_t opsqlite_libsql_prepare_statement(std::string const &name,
|
|
319
|
+
std::string const &query) {
|
|
320
|
+
check_db_open(name);
|
|
321
|
+
|
|
322
|
+
DB db = db_map[name];
|
|
323
|
+
|
|
324
|
+
libsql_stmt_t stmt;
|
|
325
|
+
|
|
326
|
+
const char *err;
|
|
327
|
+
|
|
328
|
+
int status = libsql_prepare(db.c, query.c_str(), &stmt, &err);
|
|
329
|
+
|
|
330
|
+
if (status != 0) {
|
|
331
|
+
throw std::runtime_error(err);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return stmt;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/// Base execution function, returns HostObjects to the JS environment
|
|
338
|
+
BridgeResult opsqlite_libsql_execute(
|
|
339
|
+
std::string const &name, std::string const &query,
|
|
340
|
+
const std::vector<JSVariant> *params, std::vector<DumbHostObject> *results,
|
|
341
|
+
std::shared_ptr<std::vector<SmartHostObject>> metadatas) {
|
|
342
|
+
|
|
343
|
+
check_db_open(name);
|
|
344
|
+
|
|
345
|
+
libsql_connection_t c = db_map[name].c;
|
|
346
|
+
libsql_rows_t rows;
|
|
347
|
+
libsql_row_t row;
|
|
348
|
+
libsql_stmt_t stmt;
|
|
349
|
+
int status = 0;
|
|
350
|
+
const char *err = NULL;
|
|
351
|
+
|
|
352
|
+
status = libsql_prepare(c, query.c_str(), &stmt, &err);
|
|
353
|
+
|
|
354
|
+
if (status != 0) {
|
|
355
|
+
return {.type = SQLiteError, .message = err};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (params != nullptr && params->size() > 0) {
|
|
359
|
+
opsqlite_libsql_bind_statement(stmt, params);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
status = libsql_query_stmt(stmt, &rows, &err);
|
|
363
|
+
|
|
364
|
+
if (status != 0) {
|
|
365
|
+
return {.type = SQLiteError, .message = err};
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
bool metadata_set = false;
|
|
369
|
+
|
|
370
|
+
int num_cols = libsql_column_count(rows);
|
|
371
|
+
while ((status = libsql_next_row(rows, &row, &err)) == 0) {
|
|
372
|
+
|
|
373
|
+
if (!err && !row) {
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
DumbHostObject row_host_object = DumbHostObject(metadatas);
|
|
378
|
+
|
|
379
|
+
for (int col = 0; col < num_cols; col++) {
|
|
380
|
+
int type;
|
|
381
|
+
|
|
382
|
+
libsql_column_type(rows, row, col, &type, &err);
|
|
383
|
+
|
|
384
|
+
switch (type) {
|
|
385
|
+
case LIBSQL_INT:
|
|
386
|
+
long long int_value;
|
|
387
|
+
status = libsql_get_int(row, col, &int_value, &err);
|
|
388
|
+
row_host_object.values.push_back(JSVariant(int_value));
|
|
389
|
+
break;
|
|
390
|
+
|
|
391
|
+
case LIBSQL_FLOAT:
|
|
392
|
+
double float_value;
|
|
393
|
+
status = libsql_get_float(row, col, &float_value, &err);
|
|
394
|
+
row_host_object.values.push_back(JSVariant(float_value));
|
|
395
|
+
break;
|
|
396
|
+
|
|
397
|
+
case LIBSQL_TEXT:
|
|
398
|
+
const char *text_value;
|
|
399
|
+
status = libsql_get_string(row, col, &text_value, &err);
|
|
400
|
+
row_host_object.values.push_back(JSVariant(text_value));
|
|
401
|
+
break;
|
|
402
|
+
|
|
403
|
+
case LIBSQL_BLOB: {
|
|
404
|
+
blob value_blob;
|
|
405
|
+
libsql_get_blob(row, col, &value_blob, &err);
|
|
406
|
+
uint8_t *data = new uint8_t[value_blob.len];
|
|
407
|
+
// You cannot share raw memory between native and JS
|
|
408
|
+
// always copy the data
|
|
409
|
+
memcpy(data, value_blob.ptr, value_blob.len);
|
|
410
|
+
libsql_free_blob(value_blob);
|
|
411
|
+
row_host_object.values.push_back(JSVariant(
|
|
412
|
+
ArrayBuffer{.data = std::shared_ptr<uint8_t>{data},
|
|
413
|
+
.size = static_cast<size_t>(value_blob.len)}));
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
case LIBSQL_NULL:
|
|
418
|
+
// intentional fall-through
|
|
419
|
+
default:
|
|
420
|
+
row_host_object.values.push_back(JSVariant(nullptr));
|
|
421
|
+
break;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (status != 0) {
|
|
425
|
+
fprintf(stderr, "%s\n", err);
|
|
426
|
+
throw std::runtime_error("libsql error");
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// On the first interation through the columns, set the metadata
|
|
430
|
+
if (!metadata_set && metadatas != nullptr) {
|
|
431
|
+
const char *col_name;
|
|
432
|
+
status = libsql_column_name(rows, col, &col_name, &err);
|
|
433
|
+
|
|
434
|
+
auto metadata = SmartHostObject();
|
|
435
|
+
metadata.fields.push_back(std::make_pair("name", col_name));
|
|
436
|
+
metadata.fields.push_back(std::make_pair("index", col));
|
|
437
|
+
metadata.fields.push_back(std::make_pair("type", "UNKNOWN"));
|
|
438
|
+
// metadata.fields.push_back(
|
|
439
|
+
// std::make_pair("type", type == -1 ? "UNKNOWN" :
|
|
440
|
+
// type));
|
|
441
|
+
|
|
442
|
+
metadatas->push_back(metadata);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (results != nullptr) {
|
|
447
|
+
results->push_back(row_host_object);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
metadata_set = true;
|
|
451
|
+
err = NULL;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (status != 0) {
|
|
455
|
+
fprintf(stderr, "%s\n", err);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
libsql_free_rows(rows);
|
|
459
|
+
libsql_free_stmt(stmt);
|
|
460
|
+
|
|
461
|
+
int changes = libsql_changes(c);
|
|
462
|
+
long long insert_row_id = libsql_last_insert_rowid(c);
|
|
463
|
+
|
|
464
|
+
return {.type = SQLiteOk,
|
|
465
|
+
.affectedRows = changes,
|
|
466
|
+
.insertId = static_cast<double>(insert_row_id)};
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/// Executes returning data in raw arrays, a small performance optimization
|
|
470
|
+
/// for certain use cases
|
|
471
|
+
BridgeResult
|
|
472
|
+
opsqlite_libsql_execute_raw(std::string const &name, std::string const &query,
|
|
473
|
+
const std::vector<JSVariant> *params,
|
|
474
|
+
std::vector<std::vector<JSVariant>> *results) {
|
|
475
|
+
|
|
476
|
+
check_db_open(name);
|
|
477
|
+
|
|
478
|
+
libsql_connection_t c = db_map[name].c;
|
|
479
|
+
libsql_rows_t rows;
|
|
480
|
+
libsql_row_t row;
|
|
481
|
+
libsql_stmt_t stmt;
|
|
482
|
+
int status = 0;
|
|
483
|
+
const char *err = NULL;
|
|
484
|
+
|
|
485
|
+
status = libsql_prepare(c, query.c_str(), &stmt, &err);
|
|
486
|
+
|
|
487
|
+
if (status != 0) {
|
|
488
|
+
return {.type = SQLiteError, .message = err};
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (params != nullptr && params->size() > 0) {
|
|
492
|
+
opsqlite_libsql_bind_statement(stmt, params);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
status = libsql_query_stmt(stmt, &rows, &err);
|
|
496
|
+
|
|
497
|
+
if (status != 0) {
|
|
498
|
+
return {.type = SQLiteError, .message = err};
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
int num_cols = libsql_column_count(rows);
|
|
502
|
+
while ((status = libsql_next_row(rows, &row, &err)) == 0) {
|
|
503
|
+
|
|
504
|
+
if (!err && !row) {
|
|
505
|
+
break;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
std::vector<JSVariant> row_vector;
|
|
509
|
+
|
|
510
|
+
for (int col = 0; col < num_cols; col++) {
|
|
511
|
+
int type;
|
|
512
|
+
|
|
513
|
+
libsql_column_type(rows, row, col, &type, &err);
|
|
514
|
+
|
|
515
|
+
switch (type) {
|
|
516
|
+
case LIBSQL_INT:
|
|
517
|
+
long long int_value;
|
|
518
|
+
status = libsql_get_int(row, col, &int_value, &err);
|
|
519
|
+
row_vector.push_back(JSVariant(int_value));
|
|
520
|
+
break;
|
|
521
|
+
|
|
522
|
+
case LIBSQL_FLOAT:
|
|
523
|
+
double float_value;
|
|
524
|
+
status = libsql_get_float(row, col, &float_value, &err);
|
|
525
|
+
row_vector.push_back(JSVariant(float_value));
|
|
526
|
+
break;
|
|
527
|
+
|
|
528
|
+
case LIBSQL_TEXT:
|
|
529
|
+
const char *text_value;
|
|
530
|
+
status = libsql_get_string(row, col, &text_value, &err);
|
|
531
|
+
row_vector.push_back(JSVariant(text_value));
|
|
532
|
+
break;
|
|
533
|
+
|
|
534
|
+
case LIBSQL_BLOB: {
|
|
535
|
+
blob value_blob;
|
|
536
|
+
libsql_get_blob(row, col, &value_blob, &err);
|
|
537
|
+
uint8_t *data = new uint8_t[value_blob.len];
|
|
538
|
+
// You cannot share raw memory between native and JS
|
|
539
|
+
// always copy the data
|
|
540
|
+
memcpy(data, value_blob.ptr, value_blob.len);
|
|
541
|
+
libsql_free_blob(value_blob);
|
|
542
|
+
row_vector.push_back(JSVariant(
|
|
543
|
+
ArrayBuffer{.data = std::shared_ptr<uint8_t>{data},
|
|
544
|
+
.size = static_cast<size_t>(value_blob.len)}));
|
|
545
|
+
break;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
case LIBSQL_NULL:
|
|
549
|
+
// intentional fall-through
|
|
550
|
+
default:
|
|
551
|
+
row_vector.push_back(JSVariant(nullptr));
|
|
552
|
+
break;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (status != 0) {
|
|
556
|
+
fprintf(stderr, "%s\n", err);
|
|
557
|
+
throw std::runtime_error("libsql error");
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (results != nullptr) {
|
|
562
|
+
results->push_back(row_vector);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
err = NULL;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
if (status != 0) {
|
|
569
|
+
fprintf(stderr, "%s\n", err);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
libsql_free_rows(rows);
|
|
573
|
+
libsql_free_stmt(stmt);
|
|
574
|
+
|
|
575
|
+
int changes = libsql_changes(c);
|
|
576
|
+
long long insert_row_id = libsql_last_insert_rowid(c);
|
|
577
|
+
|
|
578
|
+
return {.type = SQLiteOk,
|
|
579
|
+
.affectedRows = changes,
|
|
580
|
+
.insertId = static_cast<double>(insert_row_id)};
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
BatchResult
|
|
584
|
+
opsqlite_libsql_execute_batch(std::string const &name,
|
|
585
|
+
std::vector<BatchArguments> *commands) {
|
|
586
|
+
size_t commandCount = commands->size();
|
|
587
|
+
if (commandCount <= 0) {
|
|
588
|
+
return BatchResult{
|
|
589
|
+
.type = SQLiteError,
|
|
590
|
+
.message = "No SQL commands provided",
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
try {
|
|
595
|
+
int affectedRows = 0;
|
|
596
|
+
opsqlite_libsql_execute(name, "BEGIN EXCLUSIVE TRANSACTION", nullptr,
|
|
597
|
+
nullptr, nullptr);
|
|
598
|
+
for (int i = 0; i < commandCount; i++) {
|
|
599
|
+
auto command = commands->at(i);
|
|
600
|
+
// We do not provide a datastructure to receive query data because we
|
|
601
|
+
// don't need/want to handle this results in a batch execution
|
|
602
|
+
auto result = opsqlite_libsql_execute(
|
|
603
|
+
name, command.sql, command.params.get(), nullptr, nullptr);
|
|
604
|
+
if (result.type == SQLiteError) {
|
|
605
|
+
opsqlite_libsql_execute(name, "ROLLBACK", nullptr, nullptr, nullptr);
|
|
606
|
+
return BatchResult{
|
|
607
|
+
.type = SQLiteError,
|
|
608
|
+
.message = result.message,
|
|
609
|
+
};
|
|
610
|
+
} else {
|
|
611
|
+
affectedRows += result.affectedRows;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
opsqlite_libsql_execute(name, "COMMIT", nullptr, nullptr, nullptr);
|
|
615
|
+
return BatchResult{
|
|
616
|
+
.type = SQLiteOk,
|
|
617
|
+
.affectedRows = affectedRows,
|
|
618
|
+
.commands = static_cast<int>(commandCount),
|
|
619
|
+
};
|
|
620
|
+
} catch (std::exception &exc) {
|
|
621
|
+
opsqlite_libsql_execute(name, "ROLLBACK", nullptr, nullptr, nullptr);
|
|
622
|
+
return BatchResult{
|
|
623
|
+
.type = SQLiteError,
|
|
624
|
+
.message = exc.what(),
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
} // namespace opsqlite
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "DumbHostObject.h"
|
|
4
|
+
#include "SmartHostObject.h"
|
|
5
|
+
#include "libsql.h"
|
|
6
|
+
#include "types.h"
|
|
7
|
+
#include "utils.h"
|
|
8
|
+
#include <vector>
|
|
9
|
+
|
|
10
|
+
#define LIBSQL_INT 1
|
|
11
|
+
#define LIBSQL_FLOAT 2
|
|
12
|
+
#define LIBSQL_TEXT 3
|
|
13
|
+
#define LIBSQL_BLOB 4
|
|
14
|
+
#define LIBSQL_NULL 5
|
|
15
|
+
|
|
16
|
+
namespace opsqlite {
|
|
17
|
+
|
|
18
|
+
namespace jsi = facebook::jsi;
|
|
19
|
+
|
|
20
|
+
/// Convenience types to avoid super long types
|
|
21
|
+
typedef std::function<void(std::string dbName, std::string table_name,
|
|
22
|
+
std::string operation, int row_id)>
|
|
23
|
+
UpdateCallback;
|
|
24
|
+
typedef std::function<void(std::string dbName)> CommitCallback;
|
|
25
|
+
typedef std::function<void(std::string dbName)> RollbackCallback;
|
|
26
|
+
|
|
27
|
+
std::string opsqlite_get_db_path(std::string const &name,
|
|
28
|
+
std::string const &location);
|
|
29
|
+
|
|
30
|
+
BridgeResult opsqlite_libsql_open(std::string const &name,
|
|
31
|
+
std::string const &path);
|
|
32
|
+
|
|
33
|
+
BridgeResult opsqlite_libsql_close(std::string const &name);
|
|
34
|
+
|
|
35
|
+
BridgeResult opsqlite_libsql_remove(std::string const &name,
|
|
36
|
+
std::string const &path);
|
|
37
|
+
|
|
38
|
+
BridgeResult opsqlite_libsql_attach(std::string const &mainDBName,
|
|
39
|
+
std::string const &docPath,
|
|
40
|
+
std::string const &databaseToAttach,
|
|
41
|
+
std::string const &alias);
|
|
42
|
+
|
|
43
|
+
BridgeResult opsqlite_libsql_detach(std::string const &mainDBName,
|
|
44
|
+
std::string const &alias);
|
|
45
|
+
|
|
46
|
+
BridgeResult opsqlite_libsql_execute(
|
|
47
|
+
std::string const &name, std::string const &query,
|
|
48
|
+
const std::vector<JSVariant> *params, std::vector<DumbHostObject> *results,
|
|
49
|
+
std::shared_ptr<std::vector<SmartHostObject>> metadatas);
|
|
50
|
+
|
|
51
|
+
BridgeResult
|
|
52
|
+
opsqlite_libsql_execute_raw(std::string const &dbName, std::string const &query,
|
|
53
|
+
const std::vector<JSVariant> *params,
|
|
54
|
+
std::vector<std::vector<JSVariant>> *results);
|
|
55
|
+
|
|
56
|
+
BatchResult
|
|
57
|
+
opsqlite_libsql_execute_batch(std::string const &name,
|
|
58
|
+
std::vector<BatchArguments> *commands);
|
|
59
|
+
|
|
60
|
+
// void opsqlite_close_all();
|
|
61
|
+
|
|
62
|
+
// BridgeResult opsqlite_register_update_hook(std::string const &dbName,
|
|
63
|
+
// UpdateCallback const callback);
|
|
64
|
+
// BridgeResult opsqlite_deregister_update_hook(std::string const &dbName);
|
|
65
|
+
// BridgeResult opsqlite_register_commit_hook(std::string const &dbName,
|
|
66
|
+
// CommitCallback const callback);
|
|
67
|
+
// BridgeResult opsqlite_deregister_commit_hook(std::string const &dbName);
|
|
68
|
+
// BridgeResult opsqlite_register_rollback_hook(std::string const &dbName,
|
|
69
|
+
// RollbackCallback const
|
|
70
|
+
// callback);
|
|
71
|
+
// BridgeResult opsqlite_deregister_rollback_hook(std::string const &dbName);
|
|
72
|
+
|
|
73
|
+
libsql_stmt_t opsqlite_libsql_prepare_statement(std::string const &name,
|
|
74
|
+
std::string const &query);
|
|
75
|
+
|
|
76
|
+
void opsqlite_libsql_bind_statement(libsql_stmt_t stmt,
|
|
77
|
+
const std::vector<JSVariant> *params);
|
|
78
|
+
|
|
79
|
+
BridgeResult opsqlite_libsql_execute_prepared_statement(
|
|
80
|
+
std::string const &name, libsql_stmt_t stmt,
|
|
81
|
+
std::vector<DumbHostObject> *results,
|
|
82
|
+
std::shared_ptr<std::vector<SmartHostObject>> metadatas);
|
|
83
|
+
|
|
84
|
+
// BridgeResult opsqlite_load_extension(std::string const &db_name,
|
|
85
|
+
// std::string &path,
|
|
86
|
+
// std::string &entry_point);
|
|
87
|
+
|
|
88
|
+
} // namespace opsqlite
|