@op-engineering/op-sqlite 1.0.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/LICENSE +7 -0
- package/README.md +274 -0
- package/android/.project +17 -0
- package/android/.settings/org.eclipse.buildship.core.prefs +13 -0
- package/android/CMakeLists.txt +57 -0
- package/android/build.gradle +127 -0
- package/android/cpp-adapter.cpp +42 -0
- package/android/gradle.properties +4 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/java/com/op/sqlite/OPSQLiteBridge.java +29 -0
- package/android/src/main/java/com/op/sqlite/OPSQLiteModule.java +46 -0
- package/android/src/main/java/com/op/sqlite/OPSQLitePackage.java +26 -0
- package/cpp/DynamicHostObject.cpp +30 -0
- package/cpp/DynamicHostObject.h +30 -0
- package/cpp/ThreadPool.cpp +95 -0
- package/cpp/ThreadPool.h +46 -0
- package/cpp/bindings.cpp +430 -0
- package/cpp/bindings.h +13 -0
- package/cpp/bridge.cpp +502 -0
- package/cpp/bridge.h +34 -0
- package/cpp/logs.h +38 -0
- package/cpp/macros.h +16 -0
- package/cpp/sqlbatchexecutor.cpp +94 -0
- package/cpp/sqlbatchexecutor.h +28 -0
- package/cpp/sqlite3.c +252611 -0
- package/cpp/sqlite3.h +13257 -0
- package/cpp/utils.cpp +218 -0
- package/cpp/utils.h +55 -0
- package/ios/OPSQLite.h +8 -0
- package/ios/OPSQLite.mm +63 -0
- package/ios/OPSQLite.xcodeproj/project.pbxproj +275 -0
- package/lib/commonjs/index.js +190 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/module/index.js +183 -0
- package/lib/module/index.js.map +1 -0
- package/lib/typescript/index.d.ts +108 -0
- package/op-sqlite.podspec +39 -0
- package/package.json +79 -0
- package/src/index.ts +374 -0
package/cpp/bridge.cpp
ADDED
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
#include "bridge.h"
|
|
2
|
+
#include <sstream>
|
|
3
|
+
#include <iostream>
|
|
4
|
+
#include <sqlite3.h>
|
|
5
|
+
#include <ctime>
|
|
6
|
+
#include <unistd.h>
|
|
7
|
+
#include <sys/stat.h>
|
|
8
|
+
#include <map>
|
|
9
|
+
#include "logs.h"
|
|
10
|
+
#include "DynamicHostObject.h"
|
|
11
|
+
|
|
12
|
+
namespace osp {
|
|
13
|
+
|
|
14
|
+
std::unordered_map<std::string, sqlite3 *> dbMap = std::unordered_map<std::string, sqlite3 *>();
|
|
15
|
+
|
|
16
|
+
bool folder_exists(const std::string &foldername)
|
|
17
|
+
{
|
|
18
|
+
struct stat buffer;
|
|
19
|
+
return (stat(foldername.c_str(), &buffer) == 0);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Portable wrapper for mkdir. Internally used by mkdir()
|
|
24
|
+
* @param[in] path the full path of the directory to create.
|
|
25
|
+
* @return zero on success, otherwise -1.
|
|
26
|
+
*/
|
|
27
|
+
int _mkdir(const char *path)
|
|
28
|
+
{
|
|
29
|
+
#if _POSIX_C_SOURCE
|
|
30
|
+
return mkdir(path);
|
|
31
|
+
#else
|
|
32
|
+
return mkdir(path, 0755); // not sure if this works on mac
|
|
33
|
+
#endif
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Recursive, portable wrapper for mkdir.
|
|
38
|
+
* @param[in] path the full path of the directory to create.
|
|
39
|
+
* @return zero on success, otherwise -1.
|
|
40
|
+
*/
|
|
41
|
+
int mkdir(const char *path)
|
|
42
|
+
{
|
|
43
|
+
std::string current_level = "/";
|
|
44
|
+
std::string level;
|
|
45
|
+
std::stringstream ss(path);
|
|
46
|
+
// First line is empty because it starts with /User
|
|
47
|
+
getline(ss, level, '/');
|
|
48
|
+
// split path using slash as a separator
|
|
49
|
+
while (getline(ss, level, '/'))
|
|
50
|
+
{
|
|
51
|
+
current_level += level; // append folder to the current level
|
|
52
|
+
// create current level
|
|
53
|
+
if (!folder_exists(current_level) && _mkdir(current_level.c_str()) != 0)
|
|
54
|
+
return -1;
|
|
55
|
+
|
|
56
|
+
current_level += "/"; // don't forget to append a slash
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
inline bool file_exists(const std::string &path)
|
|
63
|
+
{
|
|
64
|
+
struct stat buffer;
|
|
65
|
+
return (stat(path.c_str(), &buffer) == 0);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
std::string get_db_path(std::string const dbName, std::string const docPath)
|
|
69
|
+
{
|
|
70
|
+
mkdir(docPath.c_str());
|
|
71
|
+
return docPath + "/" + dbName;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
BridgeResult sqliteOpenDb(std::string const dbName, std::string const docPath)
|
|
75
|
+
{
|
|
76
|
+
std::string dbPath = get_db_path(dbName, docPath);
|
|
77
|
+
|
|
78
|
+
int sqlOpenFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX;
|
|
79
|
+
|
|
80
|
+
sqlite3 *db;
|
|
81
|
+
int exit = 0;
|
|
82
|
+
exit = sqlite3_open_v2(dbPath.c_str(), &db, sqlOpenFlags, nullptr);
|
|
83
|
+
|
|
84
|
+
if (exit != SQLITE_OK)
|
|
85
|
+
{
|
|
86
|
+
return {
|
|
87
|
+
.type = SQLiteError,
|
|
88
|
+
.message = sqlite3_errmsg(db)
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
else
|
|
92
|
+
{
|
|
93
|
+
dbMap[dbName] = db;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return BridgeResult{
|
|
97
|
+
.type = SQLiteOk,
|
|
98
|
+
.affectedRows = 0
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
BridgeResult sqliteCloseDb(std::string const dbName)
|
|
103
|
+
{
|
|
104
|
+
|
|
105
|
+
if (dbMap.count(dbName) == 0)
|
|
106
|
+
{
|
|
107
|
+
return {
|
|
108
|
+
.type = SQLiteError,
|
|
109
|
+
.message = dbName + " is not open",
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
sqlite3 *db = dbMap[dbName];
|
|
114
|
+
|
|
115
|
+
sqlite3_close_v2(db);
|
|
116
|
+
|
|
117
|
+
dbMap.erase(dbName);
|
|
118
|
+
|
|
119
|
+
return BridgeResult{
|
|
120
|
+
.type = SQLiteOk,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
void sqliteCloseAll() {
|
|
125
|
+
for (auto const& x : dbMap) {
|
|
126
|
+
// In certain cases, this will return SQLITE_OK, mark the database connection as an unusable "zombie",
|
|
127
|
+
// and deallocate the connection later.
|
|
128
|
+
sqlite3_close_v2(x.second);
|
|
129
|
+
}
|
|
130
|
+
dbMap.clear();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
BridgeResult sqliteAttachDb(std::string const mainDBName, std::string const docPath, std::string const databaseToAttach, std::string const alias)
|
|
134
|
+
{
|
|
135
|
+
/**
|
|
136
|
+
* There is no need to check if mainDBName is opened because sqliteExecuteLiteral will do that.
|
|
137
|
+
* */
|
|
138
|
+
std::string dbPath = get_db_path(databaseToAttach, docPath);
|
|
139
|
+
std::string statement = "ATTACH DATABASE '" + dbPath + "' AS " + alias;
|
|
140
|
+
|
|
141
|
+
BridgeResult result = sqliteExecuteLiteral(mainDBName, statement);
|
|
142
|
+
|
|
143
|
+
if (result.type == SQLiteError)
|
|
144
|
+
{
|
|
145
|
+
return {
|
|
146
|
+
.type = SQLiteError,
|
|
147
|
+
.message = mainDBName + " was unable to attach another database: " + std::string(result.message),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
.type = SQLiteOk,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
BridgeResult sqliteDetachDb(std::string const mainDBName, std::string const alias)
|
|
156
|
+
{
|
|
157
|
+
/**
|
|
158
|
+
* There is no need to check if mainDBName is opened because sqliteExecuteLiteral will do that.
|
|
159
|
+
* */
|
|
160
|
+
std::string statement = "DETACH DATABASE " + alias;
|
|
161
|
+
BridgeResult result = sqliteExecuteLiteral(mainDBName, statement);
|
|
162
|
+
if (result.type == SQLiteError)
|
|
163
|
+
{
|
|
164
|
+
return BridgeResult{
|
|
165
|
+
.type = SQLiteError,
|
|
166
|
+
.message = mainDBName + "was unable to detach database: " + std::string(result.message),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
return BridgeResult{
|
|
170
|
+
.type = SQLiteOk,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
BridgeResult sqliteRemoveDb(std::string const dbName, std::string const docPath)
|
|
175
|
+
{
|
|
176
|
+
if (dbMap.count(dbName) == 1)
|
|
177
|
+
{
|
|
178
|
+
BridgeResult closeResult = sqliteCloseDb(dbName);
|
|
179
|
+
if (closeResult.type == SQLiteError)
|
|
180
|
+
{
|
|
181
|
+
return closeResult;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
std::string dbPath = get_db_path(dbName, docPath);
|
|
186
|
+
|
|
187
|
+
if (!file_exists(dbPath))
|
|
188
|
+
{
|
|
189
|
+
return {
|
|
190
|
+
.type = SQLiteError,
|
|
191
|
+
.message = "[op-sqlite]: Database file not found" + dbPath
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
remove(dbPath.c_str());
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
.type = SQLiteOk,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
void bindStatement(sqlite3_stmt *statement, std::vector<std::any> *values)
|
|
203
|
+
{
|
|
204
|
+
size_t size = values->size();
|
|
205
|
+
if (size <= 0)
|
|
206
|
+
{
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
for (int ii = 0; ii < size; ii++)
|
|
211
|
+
{
|
|
212
|
+
int sqIndex = ii + 1;
|
|
213
|
+
std::any value = values->at(ii);
|
|
214
|
+
|
|
215
|
+
if (value.type() == typeid(NULL) || value.type() == typeid(nullptr))
|
|
216
|
+
{
|
|
217
|
+
sqlite3_bind_null(statement, sqIndex);
|
|
218
|
+
}
|
|
219
|
+
else if (value.type() == typeid(bool))
|
|
220
|
+
{
|
|
221
|
+
sqlite3_bind_int(statement, sqIndex, std::any_cast<bool>(value));
|
|
222
|
+
}
|
|
223
|
+
else if (value.type() == typeid(int))
|
|
224
|
+
{
|
|
225
|
+
sqlite3_bind_int(statement, sqIndex, std::any_cast<int>(value));
|
|
226
|
+
}
|
|
227
|
+
else if (value.type() == typeid(long long))
|
|
228
|
+
{
|
|
229
|
+
sqlite3_bind_double(statement, sqIndex, std::any_cast<long long>(value));
|
|
230
|
+
}
|
|
231
|
+
else if (value.type() == typeid(double))
|
|
232
|
+
{
|
|
233
|
+
sqlite3_bind_double(statement, sqIndex, std::any_cast<double>(value));
|
|
234
|
+
}
|
|
235
|
+
else if (value.type() == typeid(std::string))
|
|
236
|
+
{
|
|
237
|
+
std::string str = std::any_cast<std::string>(value);
|
|
238
|
+
sqlite3_bind_text(statement, sqIndex, str.c_str(), str.length(), SQLITE_TRANSIENT);
|
|
239
|
+
} else {
|
|
240
|
+
throw std::invalid_argument("Unsupported scalar type, cannot convert to JSI Value");
|
|
241
|
+
}
|
|
242
|
+
// else if (value.type() == typeid(const char*))
|
|
243
|
+
// {
|
|
244
|
+
// sqlite3_bind_text(statement, sqIndex, str.c_str(), str.length(), SQLITE_TRANSIENT);
|
|
245
|
+
// return jsi::String::createFromAscii(rt, std::any_cast<const char*>(value));
|
|
246
|
+
// }
|
|
247
|
+
// TODO Add support for array buffers
|
|
248
|
+
// else if (value.isObject())
|
|
249
|
+
// {
|
|
250
|
+
// auto obj = value.asObject(rt);
|
|
251
|
+
// if(obj.isArrayBuffer(rt)) {
|
|
252
|
+
// auto buf = obj.getArrayBuffer(rt);
|
|
253
|
+
// sqlite3_bind_blob(statement, sqIndex, buf.data(rt), buf.size(rt), SQLITE_STATIC);
|
|
254
|
+
// }
|
|
255
|
+
//
|
|
256
|
+
// else if (dataType == ARRAY_BUFFER)
|
|
257
|
+
// {
|
|
258
|
+
//
|
|
259
|
+
// }
|
|
260
|
+
// sqlite3_bind_blob(statement, sqIndex, value.arrayBufferValue.get(), value.arrayBufferSize, SQLITE_STATIC);
|
|
261
|
+
// }
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
BridgeResult sqliteExecute(std::string const dbName,
|
|
269
|
+
std::string const &query,
|
|
270
|
+
std::vector<std::any> *params,
|
|
271
|
+
std::vector<std::shared_ptr<DynamicHostObject>> *results,
|
|
272
|
+
std::vector<std::shared_ptr<DynamicHostObject>> *metadatas)
|
|
273
|
+
{
|
|
274
|
+
|
|
275
|
+
if (dbMap.count(dbName) == 0)
|
|
276
|
+
{
|
|
277
|
+
return {
|
|
278
|
+
.type = SQLiteError,
|
|
279
|
+
.message = "[op-sqlite]: Database " + dbName + " is not open",
|
|
280
|
+
.affectedRows = 0
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
sqlite3 *db = dbMap[dbName];
|
|
285
|
+
|
|
286
|
+
sqlite3_stmt *statement;
|
|
287
|
+
|
|
288
|
+
int statementStatus = sqlite3_prepare_v2(db, query.c_str(), -1, &statement, NULL);
|
|
289
|
+
|
|
290
|
+
if (statementStatus == SQLITE_OK) // statemnet is correct, bind the passed parameters
|
|
291
|
+
{
|
|
292
|
+
bindStatement(statement, params);
|
|
293
|
+
}
|
|
294
|
+
else
|
|
295
|
+
{
|
|
296
|
+
const char *message = sqlite3_errmsg(db);
|
|
297
|
+
return {
|
|
298
|
+
.type = SQLiteError,
|
|
299
|
+
.message = "[op-sqlite] SQL execution error: " + std::string(message),
|
|
300
|
+
.affectedRows = 0};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
bool isConsuming = true;
|
|
304
|
+
bool isFailed = false;
|
|
305
|
+
|
|
306
|
+
int result, i, count, column_type;
|
|
307
|
+
std::string column_name, column_declared_type;
|
|
308
|
+
std::shared_ptr<DynamicHostObject> row;
|
|
309
|
+
|
|
310
|
+
while (isConsuming)
|
|
311
|
+
{
|
|
312
|
+
result = sqlite3_step(statement);
|
|
313
|
+
|
|
314
|
+
switch (result)
|
|
315
|
+
{
|
|
316
|
+
case SQLITE_ROW:
|
|
317
|
+
if(results == NULL)
|
|
318
|
+
{
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
i = 0;
|
|
323
|
+
row = std::make_shared<DynamicHostObject>();
|
|
324
|
+
count = sqlite3_column_count(statement);
|
|
325
|
+
|
|
326
|
+
while (i < count)
|
|
327
|
+
{
|
|
328
|
+
column_type = sqlite3_column_type(statement, i);
|
|
329
|
+
column_name = sqlite3_column_name(statement, i);
|
|
330
|
+
|
|
331
|
+
switch (column_type)
|
|
332
|
+
{
|
|
333
|
+
|
|
334
|
+
case SQLITE_INTEGER:
|
|
335
|
+
{
|
|
336
|
+
/**
|
|
337
|
+
* Warning this will loose precision because JS can
|
|
338
|
+
* only represent Integers up to 53 bits
|
|
339
|
+
*/
|
|
340
|
+
double column_value = sqlite3_column_double(statement, i);
|
|
341
|
+
row->fields[column_name] = std::any(column_value);
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
case SQLITE_FLOAT:
|
|
346
|
+
{
|
|
347
|
+
double column_value = sqlite3_column_double(statement, i);
|
|
348
|
+
row->fields[column_name] = std::any(column_value);
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
case SQLITE_TEXT:
|
|
353
|
+
{
|
|
354
|
+
const char *column_value = reinterpret_cast<const char *>(sqlite3_column_text(statement, i));
|
|
355
|
+
int byteLen = sqlite3_column_bytes(statement, i);
|
|
356
|
+
// Specify length too; in case string contains NULL in the middle (which SQLite supports!)
|
|
357
|
+
row->fields[column_name] = std::any(std::string(column_value, byteLen));
|
|
358
|
+
break;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
case SQLITE_BLOB:
|
|
362
|
+
{
|
|
363
|
+
int blob_size = sqlite3_column_bytes(statement, i);
|
|
364
|
+
const void *blob = sqlite3_column_blob(statement, i);
|
|
365
|
+
uint8_t *data = new uint8_t[blob_size];
|
|
366
|
+
memcpy(data, blob, blob_size);
|
|
367
|
+
// TODO array buffer support
|
|
368
|
+
// row[column_name] = createArrayBufferQuickValue(data, blob_size);
|
|
369
|
+
break;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
case SQLITE_NULL:
|
|
373
|
+
// Intentionally left blank
|
|
374
|
+
|
|
375
|
+
default:
|
|
376
|
+
row->fields[column_name] = std::any(NULL);
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
i++;
|
|
380
|
+
}
|
|
381
|
+
results->push_back(row);
|
|
382
|
+
break;
|
|
383
|
+
|
|
384
|
+
case SQLITE_DONE:
|
|
385
|
+
if(metadatas != nullptr)
|
|
386
|
+
{
|
|
387
|
+
i = 0;
|
|
388
|
+
count = sqlite3_column_count(statement);
|
|
389
|
+
while (i < count)
|
|
390
|
+
{
|
|
391
|
+
column_name = sqlite3_column_name(statement, i);
|
|
392
|
+
const char *type = sqlite3_column_decltype(statement, i);
|
|
393
|
+
auto metadata = std::make_shared<DynamicHostObject>();
|
|
394
|
+
metadata->fields["name"] = column_name;
|
|
395
|
+
metadata->fields["index"] = i;
|
|
396
|
+
metadata->fields["type"] = type;
|
|
397
|
+
|
|
398
|
+
i++;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
isConsuming = false;
|
|
402
|
+
break;
|
|
403
|
+
|
|
404
|
+
default:
|
|
405
|
+
isFailed = true;
|
|
406
|
+
isConsuming = false;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
sqlite3_finalize(statement);
|
|
411
|
+
|
|
412
|
+
if (isFailed)
|
|
413
|
+
{
|
|
414
|
+
const char *message = sqlite3_errmsg(db);
|
|
415
|
+
return {
|
|
416
|
+
.type = SQLiteError,
|
|
417
|
+
.message = "[op-sqlite] SQL execution error: " + std::string(message),
|
|
418
|
+
.affectedRows = 0,
|
|
419
|
+
.insertId = 0
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
int changedRowCount = sqlite3_changes(db);
|
|
424
|
+
long long latestInsertRowId = sqlite3_last_insert_rowid(db);
|
|
425
|
+
|
|
426
|
+
return {
|
|
427
|
+
.type = SQLiteOk,
|
|
428
|
+
.affectedRows = changedRowCount,
|
|
429
|
+
.insertId = static_cast<double>(latestInsertRowId)};
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
BridgeResult sqliteExecuteLiteral(std::string const dbName, std::string const &query)
|
|
433
|
+
{
|
|
434
|
+
// Check if db connection is opened
|
|
435
|
+
if (dbMap.count(dbName) == 0)
|
|
436
|
+
{
|
|
437
|
+
return {
|
|
438
|
+
SQLiteError,
|
|
439
|
+
"[op-sqlite] Database not opened: " + dbName
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
sqlite3 *db = dbMap[dbName];
|
|
444
|
+
sqlite3_stmt *statement;
|
|
445
|
+
|
|
446
|
+
// Compile and move result into statement memory spot
|
|
447
|
+
int statementStatus = sqlite3_prepare_v2(db, query.c_str(), -1, &statement, NULL);
|
|
448
|
+
|
|
449
|
+
if (statementStatus != SQLITE_OK)
|
|
450
|
+
{
|
|
451
|
+
const char *message = sqlite3_errmsg(db);
|
|
452
|
+
return {
|
|
453
|
+
SQLiteError,
|
|
454
|
+
"[op-sqlite] SQL execution error: " + std::string(message),
|
|
455
|
+
0};
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
bool isConsuming = true;
|
|
459
|
+
bool isFailed = false;
|
|
460
|
+
|
|
461
|
+
int result;
|
|
462
|
+
std::string column_name;
|
|
463
|
+
|
|
464
|
+
while (isConsuming)
|
|
465
|
+
{
|
|
466
|
+
result = sqlite3_step(statement);
|
|
467
|
+
|
|
468
|
+
switch (result)
|
|
469
|
+
{
|
|
470
|
+
case SQLITE_ROW:
|
|
471
|
+
isConsuming = true;
|
|
472
|
+
break;
|
|
473
|
+
|
|
474
|
+
case SQLITE_DONE:
|
|
475
|
+
isConsuming = false;
|
|
476
|
+
break;
|
|
477
|
+
|
|
478
|
+
default:
|
|
479
|
+
isFailed = true;
|
|
480
|
+
isConsuming = false;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
sqlite3_finalize(statement);
|
|
485
|
+
|
|
486
|
+
if (isFailed)
|
|
487
|
+
{
|
|
488
|
+
const char *message = sqlite3_errmsg(db);
|
|
489
|
+
return {
|
|
490
|
+
SQLiteError,
|
|
491
|
+
"[op-sqlite] SQL execution error: " + std::string(message),
|
|
492
|
+
0};
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
int changedRowCount = sqlite3_changes(db);
|
|
496
|
+
return {
|
|
497
|
+
SQLiteOk,
|
|
498
|
+
"",
|
|
499
|
+
changedRowCount};
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
}
|
package/cpp/bridge.h
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#ifndef bridge_h
|
|
2
|
+
#define bridge_h
|
|
3
|
+
|
|
4
|
+
#include "utils.h"
|
|
5
|
+
#include <vector>
|
|
6
|
+
#include "DynamicHostObject.h"
|
|
7
|
+
|
|
8
|
+
namespace osp {
|
|
9
|
+
|
|
10
|
+
namespace jsi = facebook::jsi;
|
|
11
|
+
|
|
12
|
+
BridgeResult sqliteOpenDb(std::string const dbName, std::string const docPath);
|
|
13
|
+
|
|
14
|
+
BridgeResult sqliteCloseDb(std::string const dbName);
|
|
15
|
+
|
|
16
|
+
BridgeResult sqliteRemoveDb(std::string const dbName, std::string const docPath);
|
|
17
|
+
|
|
18
|
+
BridgeResult sqliteAttachDb(std::string const mainDBName, std::string const docPath, std::string const databaseToAttach, std::string const alias);
|
|
19
|
+
|
|
20
|
+
BridgeResult sqliteDetachDb(std::string const mainDBName, std::string const alias);
|
|
21
|
+
|
|
22
|
+
BridgeResult sqliteExecute(std::string const dbName,
|
|
23
|
+
std::string const &query,
|
|
24
|
+
std::vector<std::any> *values,
|
|
25
|
+
std::vector<std::shared_ptr<DynamicHostObject>> *result,
|
|
26
|
+
std::vector<std::shared_ptr<DynamicHostObject>> *metadata);
|
|
27
|
+
|
|
28
|
+
BridgeResult sqliteExecuteLiteral(std::string const dbName, std::string const &query);
|
|
29
|
+
|
|
30
|
+
void sqliteCloseAll();
|
|
31
|
+
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
#endif /* bridge_h */
|
package/cpp/logs.h
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#ifdef ANDROID
|
|
2
|
+
// LOGS ANDROID
|
|
3
|
+
#include <android/log.h>
|
|
4
|
+
#define LOG_TAG "op-sqlite"
|
|
5
|
+
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
|
|
6
|
+
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
|
|
7
|
+
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
|
8
|
+
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
|
|
9
|
+
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
|
10
|
+
#define LOGSIMPLE(...)
|
|
11
|
+
#else
|
|
12
|
+
// LOGS NO ANDROID
|
|
13
|
+
#include <stdio.h>
|
|
14
|
+
#define LOG_TAG "op-sqlite"
|
|
15
|
+
#define LOGV(...) \
|
|
16
|
+
printf(" "); \
|
|
17
|
+
printf(__VA_ARGS__); \
|
|
18
|
+
printf("\t - <%s> \n", LOG_TAG);
|
|
19
|
+
#define LOGD(...) \
|
|
20
|
+
printf(" "); \
|
|
21
|
+
printf(__VA_ARGS__); \
|
|
22
|
+
printf("\t - <%s> \n", LOG_TAG);
|
|
23
|
+
#define LOGI(...) \
|
|
24
|
+
printf(" "); \
|
|
25
|
+
printf(__VA_ARGS__); \
|
|
26
|
+
printf("\t - <%s> \n", LOG_TAG);
|
|
27
|
+
#define LOGW(...) \
|
|
28
|
+
printf(" * Warning: "); \
|
|
29
|
+
printf(__VA_ARGS__); \
|
|
30
|
+
printf("\t - <%s> \n", LOG_TAG);
|
|
31
|
+
#define LOGE(...) \
|
|
32
|
+
printf(" *** Error: "); \
|
|
33
|
+
printf(__VA_ARGS__); \
|
|
34
|
+
printf("\t - <%s> \n", LOG_TAG);
|
|
35
|
+
#define LOGSIMPLE(...) \
|
|
36
|
+
printf(" "); \
|
|
37
|
+
printf(__VA_ARGS__);
|
|
38
|
+
#endif // ANDROID
|
package/cpp/macros.h
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#ifndef macros_h
|
|
2
|
+
#define macros_h
|
|
3
|
+
|
|
4
|
+
#define HOSTFN(name, basecount) \
|
|
5
|
+
jsi::Function::createFromHostFunction( \
|
|
6
|
+
rt, \
|
|
7
|
+
jsi::PropNameID::forAscii(rt, name), \
|
|
8
|
+
basecount, \
|
|
9
|
+
[=](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value
|
|
10
|
+
|
|
11
|
+
#define JSIFN(capture) \
|
|
12
|
+
capture(jsi::Runtime &runtime, const jsi::Value &thisValue, \
|
|
13
|
+
const jsi::Value *arguments, size_t count) \
|
|
14
|
+
->jsi::Value
|
|
15
|
+
|
|
16
|
+
#endif /* macros_h */
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#include "sqlbatchexecutor.h"
|
|
2
|
+
|
|
3
|
+
namespace osp {
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Batch execution implementation
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
void toBatchArguments(jsi::Runtime &rt, jsi::Array const &batchParams, std::vector<BatchArguments> *commands)
|
|
10
|
+
{
|
|
11
|
+
for (int i = 0; i < batchParams.length(rt); i++)
|
|
12
|
+
{
|
|
13
|
+
const jsi::Array &command = batchParams.getValueAtIndex(rt, i).asObject(rt).asArray(rt);
|
|
14
|
+
if (command.length(rt) == 0)
|
|
15
|
+
{
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const std::string query = command.getValueAtIndex(rt, 0).asString(rt).utf8(rt);
|
|
20
|
+
const jsi::Value &commandParams = command.length(rt) > 1 ? command.getValueAtIndex(rt, 1) : jsi::Value::undefined();
|
|
21
|
+
if (!commandParams.isUndefined() && commandParams.asObject(rt).isArray(rt) && commandParams.asObject(rt).asArray(rt).length(rt) > 0 && commandParams.asObject(rt).asArray(rt).getValueAtIndex(rt, 0).isObject())
|
|
22
|
+
{
|
|
23
|
+
// This arguments is an array of arrays, like a batch update of a single sql command.
|
|
24
|
+
const jsi::Array &batchUpdateParams = commandParams.asObject(rt).asArray(rt);
|
|
25
|
+
for (int x = 0; x < batchUpdateParams.length(rt); x++)
|
|
26
|
+
{
|
|
27
|
+
const jsi::Value &p = batchUpdateParams.getValueAtIndex(rt, x);
|
|
28
|
+
auto params = std::make_shared<std::vector<std::any>>(toAnyVec(rt, p));
|
|
29
|
+
commands->push_back({
|
|
30
|
+
query,
|
|
31
|
+
params
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else
|
|
36
|
+
{
|
|
37
|
+
auto params = std::make_shared<std::vector<std::any>>(toAnyVec(rt, commandParams));
|
|
38
|
+
commands->push_back({
|
|
39
|
+
query,
|
|
40
|
+
params
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
BatchResult sqliteExecuteBatch(std::string dbName, std::vector<BatchArguments> *commands)
|
|
47
|
+
{
|
|
48
|
+
size_t commandCount = commands->size();
|
|
49
|
+
if(commandCount <= 0)
|
|
50
|
+
{
|
|
51
|
+
return BatchResult {
|
|
52
|
+
.type = SQLiteError,
|
|
53
|
+
.message = "No SQL commands provided",
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
try
|
|
58
|
+
{
|
|
59
|
+
int affectedRows = 0;
|
|
60
|
+
sqliteExecuteLiteral(dbName, "BEGIN EXCLUSIVE TRANSACTION");
|
|
61
|
+
for(int i = 0; i < commandCount; i++) {
|
|
62
|
+
auto command = commands->at(i);
|
|
63
|
+
// We do not provide a datastructure to receive query data because we don't need/want to handle this results in a batch execution
|
|
64
|
+
auto result = sqliteExecute(dbName, command.sql, command.params.get(), nullptr, nullptr);
|
|
65
|
+
if(result.type == SQLiteError)
|
|
66
|
+
{
|
|
67
|
+
sqliteExecuteLiteral(dbName, "ROLLBACK");
|
|
68
|
+
return BatchResult {
|
|
69
|
+
.type = SQLiteError,
|
|
70
|
+
.message = result.message,
|
|
71
|
+
};
|
|
72
|
+
} else
|
|
73
|
+
{
|
|
74
|
+
affectedRows += result.affectedRows;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
sqliteExecuteLiteral(dbName, "COMMIT");
|
|
78
|
+
return BatchResult {
|
|
79
|
+
.type = SQLiteOk,
|
|
80
|
+
.affectedRows = affectedRows,
|
|
81
|
+
.commands = static_cast<int>(commandCount),
|
|
82
|
+
};
|
|
83
|
+
} catch(std::exception &exc)
|
|
84
|
+
{
|
|
85
|
+
sqliteExecuteLiteral(dbName, "ROLLBACK");
|
|
86
|
+
return BatchResult {
|
|
87
|
+
.type = SQLiteError,
|
|
88
|
+
.message = exc.what(),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
}
|