@op-engineering/op-sqlite 2.0.0 → 2.0.2

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/bridge.cpp CHANGED
@@ -1,706 +1,768 @@
1
1
  #include "bridge.h"
2
- #include <sstream>
3
- #include <iostream>
4
- #include <sqlite3.h>
2
+ #include "DumbHostObject.h"
3
+ #include "SmartHostObject.h"
4
+ #include "logs.h"
5
5
  #include <ctime>
6
- #include <unistd.h>
6
+ #include <sstream>
7
7
  #include <sys/stat.h>
8
+ #include <unistd.h>
8
9
  #include <unordered_map>
9
- #include "logs.h"
10
- #include "DynamicHostObject.h"
11
- #include "DumbHostObject.h"
12
10
  #include <variant>
13
11
 
14
12
  namespace opsqlite {
15
13
 
16
- std::unordered_map<std::string, sqlite3 *> dbMap = std::unordered_map<std::string, sqlite3 *>();
17
- std::unordered_map<
14
+ std::unordered_map<std::string, sqlite3 *> dbMap =
15
+ std::unordered_map<std::string, sqlite3 *>();
16
+ std::unordered_map<std::string,
17
+ std::function<void(std::string dbName, std::string tableName,
18
+ std::string operation, int rowId)>>
19
+ updateCallbackMap = std::unordered_map<
18
20
  std::string,
19
- std::function<void (std::string dbName, std::string tableName, std::string operation, int rowId)>> updateCallbackMap =
20
- std::unordered_map<
21
- std::string,
22
- std::function<void (std::string dbName, std::string tableName, std::string operation, int rowId)>>();
23
-
24
- std::unordered_map<
25
- std::string,
26
- std::function<void (std::string dbName)>> commitCallbackMap =
27
- std::unordered_map<std::string, std::function<void (std::string dbName)>>();
21
+ std::function<void(std::string dbName, std::string tableName,
22
+ std::string operation, int rowId)>>();
23
+
24
+ std::unordered_map<std::string, std::function<void(std::string dbName)>>
25
+ commitCallbackMap =
26
+ std::unordered_map<std::string,
27
+ std::function<void(std::string dbName)>>();
28
+
29
+ std::unordered_map<std::string, std::function<void(std::string dbName)>>
30
+ rollbackCallbackMap =
31
+ std::unordered_map<std::string,
32
+ std::function<void(std::string dbName)>>();
33
+
34
+ bool folder_exists(const std::string &foldername) {
35
+ struct stat buffer;
36
+ return (stat(foldername.c_str(), &buffer) == 0);
37
+ }
28
38
 
29
- std::unordered_map<
30
- std::string,
31
- std::function<void (std::string dbName)>> rollbackCallbackMap =
32
- std::unordered_map<std::string, std::function<void (std::string dbName)>>();
39
+ /**
40
+ * Portable wrapper for mkdir. Internally used by mkdir()
41
+ * @param[in] path the full path of the directory to create.
42
+ * @return zero on success, otherwise -1.
43
+ */
44
+ int _mkdir(const char *path) {
45
+ #if _POSIX_C_SOURCE
46
+ return mkdir(path);
47
+ #else
48
+ return mkdir(path, 0755); // not sure if this works on mac
49
+ #endif
50
+ }
33
51
 
34
- bool folder_exists(const std::string &foldername)
35
- {
36
- struct stat buffer;
37
- return (stat(foldername.c_str(), &buffer) == 0);
38
- }
52
+ /**
53
+ * Recursive, portable wrapper for mkdir.
54
+ * @param[in] path the full path of the directory to create.
55
+ * @return zero on success, otherwise -1.
56
+ */
57
+ int mkdir(const char *path) {
58
+ std::string current_level = "/";
59
+ std::string level;
60
+ std::stringstream ss(path);
61
+ // First line is empty because it starts with /User
62
+ getline(ss, level, '/');
63
+ // split path using slash as a separator
64
+ while (getline(ss, level, '/')) {
65
+ current_level += level; // append folder to the current level
66
+ // create current level
67
+ if (!folder_exists(current_level) && _mkdir(current_level.c_str()) != 0)
68
+ return -1;
69
+
70
+ current_level += "/"; // don't forget to append a slash
71
+ }
72
+
73
+ return 0;
74
+ }
39
75
 
40
- /**
41
- * Portable wrapper for mkdir. Internally used by mkdir()
42
- * @param[in] path the full path of the directory to create.
43
- * @return zero on success, otherwise -1.
44
- */
45
- int _mkdir(const char *path)
46
- {
47
- #if _POSIX_C_SOURCE
48
- return mkdir(path);
49
- #else
50
- return mkdir(path, 0755); // not sure if this works on mac
51
- #endif
52
- }
76
+ inline bool file_exists(const std::string &path) {
77
+ struct stat buffer;
78
+ return (stat(path.c_str(), &buffer) == 0);
79
+ }
53
80
 
54
- /**
55
- * Recursive, portable wrapper for mkdir.
56
- * @param[in] path the full path of the directory to create.
57
- * @return zero on success, otherwise -1.
58
- */
59
- int mkdir(const char *path)
60
- {
61
- std::string current_level = "/";
62
- std::string level;
63
- std::stringstream ss(path);
64
- // First line is empty because it starts with /User
65
- getline(ss, level, '/');
66
- // split path using slash as a separator
67
- while (getline(ss, level, '/'))
68
- {
69
- current_level += level; // append folder to the current level
70
- // create current level
71
- if (!folder_exists(current_level) && _mkdir(current_level.c_str()) != 0)
72
- return -1;
73
-
74
- current_level += "/"; // don't forget to append a slash
75
- }
76
-
77
- return 0;
78
- }
81
+ std::string get_db_path(std::string const dbName, std::string const lastPath) {
82
+ if (lastPath == ":memory:") {
83
+ return lastPath;
84
+ }
85
+ mkdir(lastPath.c_str());
86
+ return lastPath + "/" + dbName;
87
+ }
79
88
 
80
- inline bool file_exists(const std::string &path)
81
- {
82
- struct stat buffer;
83
- return (stat(path.c_str(), &buffer) == 0);
84
- }
89
+ BridgeResult sqliteOpenDb(std::string const dbName,
90
+ std::string const lastPath) {
91
+ std::string dbPath = get_db_path(dbName, lastPath);
85
92
 
86
- std::string get_db_path(std::string const dbName, std::string const lastPath)
87
- {
88
- if(lastPath == ":memory:") {
89
- return lastPath;
90
- }
91
- mkdir(lastPath.c_str());
92
- return lastPath + "/" + dbName;
93
- }
93
+ int sqlOpenFlags =
94
+ SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX;
94
95
 
95
- BridgeResult sqliteOpenDb(std::string const dbName, std::string const lastPath)
96
- {
97
- std::string dbPath = get_db_path(dbName, lastPath);
98
-
99
- int sqlOpenFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX;
100
-
101
- sqlite3 *db;
102
- int exit = 0;
103
- exit = sqlite3_open_v2(dbPath.c_str(), &db, sqlOpenFlags, nullptr);
104
-
105
- if (exit != SQLITE_OK)
106
- {
107
- return {
108
- .type = SQLiteError,
109
- .message = sqlite3_errmsg(db)
110
- };
111
- }
96
+ sqlite3 *db;
97
+ int exit = 0;
98
+ exit = sqlite3_open_v2(dbPath.c_str(), &db, sqlOpenFlags, nullptr);
99
+
100
+ if (exit != SQLITE_OK) {
101
+ return {.type = SQLiteError, .message = sqlite3_errmsg(db)};
102
+ }
103
+
104
+ dbMap[dbName] = db;
105
+
106
+ return BridgeResult{.type = SQLiteOk, .affectedRows = 0};
107
+ }
108
+
109
+ BridgeResult sqliteCloseDb(std::string const dbName) {
110
+
111
+ if (dbMap.count(dbName) == 0) {
112
+ return {
113
+ .type = SQLiteError,
114
+ .message = dbName + " is not open",
115
+ };
116
+ }
117
+
118
+ sqlite3 *db = dbMap[dbName];
112
119
 
113
- dbMap[dbName] = db;
114
-
115
- return BridgeResult{
116
- .type = SQLiteOk,
117
- .affectedRows = 0
118
- };
120
+ sqlite3_close_v2(db);
121
+
122
+ dbMap.erase(dbName);
123
+
124
+ return BridgeResult{
125
+ .type = SQLiteOk,
126
+ };
127
+ }
128
+
129
+ BridgeResult sqliteAttachDb(std::string const mainDBName,
130
+ std::string const docPath,
131
+ std::string const databaseToAttach,
132
+ std::string const alias) {
133
+ /**
134
+ * There is no need to check if mainDBName is opened because
135
+ * sqliteExecuteLiteral will do that.
136
+ * */
137
+ std::string dbPath = get_db_path(databaseToAttach, docPath);
138
+ std::string statement = "ATTACH DATABASE '" + dbPath + "' AS " + alias;
139
+
140
+ BridgeResult result = sqliteExecuteLiteral(mainDBName, statement);
141
+
142
+ if (result.type == SQLiteError) {
143
+ return {
144
+ .type = SQLiteError,
145
+ .message = mainDBName + " was unable to attach another database: " +
146
+ std::string(result.message),
147
+ };
148
+ }
149
+ return {
150
+ .type = SQLiteOk,
151
+ };
152
+ }
153
+
154
+ BridgeResult sqliteDetachDb(std::string const mainDBName,
155
+ std::string const alias) {
156
+ /**
157
+ * There is no need to check if mainDBName is opened because
158
+ * sqliteExecuteLiteral will do that.
159
+ * */
160
+ std::string statement = "DETACH DATABASE " + alias;
161
+ BridgeResult result = sqliteExecuteLiteral(mainDBName, statement);
162
+ if (result.type == SQLiteError) {
163
+ return BridgeResult{
164
+ .type = SQLiteError,
165
+ .message = mainDBName + "was unable to detach database: " +
166
+ std::string(result.message),
167
+ };
168
+ }
169
+ return BridgeResult{
170
+ .type = SQLiteOk,
171
+ };
172
+ }
173
+
174
+ BridgeResult sqliteRemoveDb(std::string const dbName,
175
+ std::string const docPath) {
176
+ if (dbMap.count(dbName) == 1) {
177
+ BridgeResult closeResult = sqliteCloseDb(dbName);
178
+ if (closeResult.type == SQLiteError) {
179
+ return closeResult;
119
180
  }
181
+ }
120
182
 
121
- BridgeResult sqliteCloseDb(std::string const dbName)
122
- {
123
-
124
- if (dbMap.count(dbName) == 0)
125
- {
126
- return {
127
- .type = SQLiteError,
128
- .message = dbName + " is not open",
129
- };
130
- }
131
-
132
- sqlite3 *db = dbMap[dbName];
133
-
134
- sqlite3_close_v2(db);
135
-
136
- dbMap.erase(dbName);
137
-
138
- return BridgeResult{
139
- .type = SQLiteOk,
140
- };
183
+ std::string dbPath = get_db_path(dbName, docPath);
184
+
185
+ if (!file_exists(dbPath)) {
186
+ return {.type = SQLiteError,
187
+ .message = "[op-sqlite]: Database file not found" + dbPath};
188
+ }
189
+
190
+ remove(dbPath.c_str());
191
+
192
+ return {
193
+ .type = SQLiteOk,
194
+ };
195
+ }
196
+
197
+ inline void bindStatement(sqlite3_stmt *statement,
198
+ const std::vector<JSVariant> *values) {
199
+ size_t size = values->size();
200
+
201
+ for (int ii = 0; ii < size; ii++) {
202
+ int sqIndex = ii + 1;
203
+ JSVariant value = values->at(ii);
204
+
205
+ if (std::holds_alternative<bool>(value)) {
206
+ sqlite3_bind_int(statement, sqIndex, std::get<bool>(value));
207
+ } else if (std::holds_alternative<int>(value)) {
208
+ sqlite3_bind_int(statement, sqIndex, std::get<int>(value));
209
+ } else if (std::holds_alternative<long long>(value)) {
210
+ sqlite3_bind_double(statement, sqIndex, std::get<long long>(value));
211
+ } else if (std::holds_alternative<double>(value)) {
212
+ sqlite3_bind_double(statement, sqIndex, std::get<double>(value));
213
+ } else if (std::holds_alternative<std::string>(value)) {
214
+ std::string str = std::get<std::string>(value);
215
+ sqlite3_bind_text(statement, sqIndex, str.c_str(), str.length(),
216
+ SQLITE_TRANSIENT);
217
+ } else if (std::holds_alternative<ArrayBuffer>(value)) {
218
+ ArrayBuffer buffer = std::get<ArrayBuffer>(value);
219
+ sqlite3_bind_blob(statement, sqIndex, buffer.data.get(), buffer.size,
220
+ SQLITE_STATIC);
221
+ } else {
222
+ sqlite3_bind_null(statement, sqIndex);
141
223
  }
224
+ }
225
+ }
226
+
227
+ void sqlite_bind_statement(sqlite3_stmt *statement,
228
+ const std::vector<JSVariant> *params) {
229
+ bindStatement(statement, params);
230
+ }
231
+
232
+ BridgeResult sqlite_execute_prepared_statement(
233
+ std::string const dbName, sqlite3_stmt *statement,
234
+ std::vector<DumbHostObject> *results,
235
+ std::shared_ptr<std::vector<SmartHostObject>> metadatas) {
236
+ if (dbMap.find(dbName) == dbMap.end()) {
237
+ return {.type = SQLiteError,
238
+ .message = "[op-sqlite]: Database " + dbName + " is not open"};
239
+ }
240
+
241
+ sqlite3_reset(statement);
242
+
243
+ sqlite3 *db = dbMap[dbName];
244
+
245
+ const char *errorMessage;
246
+
247
+ bool isConsuming = true;
248
+ bool isFailed = false;
249
+
250
+ int result = SQLITE_OK;
251
+
252
+ isConsuming = true;
253
+
254
+ int i, count, column_type;
255
+ std::string column_name, column_declared_type;
256
+
257
+ while (isConsuming) {
258
+ result = sqlite3_step(statement);
259
+
260
+ switch (result) {
261
+ case SQLITE_ROW: {
262
+ if (results == NULL) {
263
+ break;
264
+ }
265
+
266
+ i = 0;
267
+ DumbHostObject row = DumbHostObject(metadatas);
268
+
269
+ count = sqlite3_column_count(statement);
270
+
271
+ while (i < count) {
272
+ column_type = sqlite3_column_type(statement, i);
142
273
 
143
- BridgeResult sqliteAttachDb(std::string const mainDBName, std::string const docPath, std::string const databaseToAttach, std::string const alias)
144
- {
145
- /**
146
- * There is no need to check if mainDBName is opened because sqliteExecuteLiteral will do that.
147
- * */
148
- std::string dbPath = get_db_path(databaseToAttach, docPath);
149
- std::string statement = "ATTACH DATABASE '" + dbPath + "' AS " + alias;
150
-
151
- BridgeResult result = sqliteExecuteLiteral(mainDBName, statement);
152
-
153
- if (result.type == SQLiteError)
154
- {
155
- return {
156
- .type = SQLiteError,
157
- .message = mainDBName + " was unable to attach another database: " + std::string(result.message),
158
- };
274
+ switch (column_type) {
275
+ case SQLITE_INTEGER: {
276
+ /**
277
+ * Warning this will loose precision because JS can
278
+ * only represent Integers up to 53 bits
279
+ */
280
+ double column_value = sqlite3_column_double(statement, i);
281
+ row.values.push_back(JSVariant(column_value));
282
+ break;
159
283
  }
160
- return {
161
- .type = SQLiteOk,
162
- };
163
- }
164
284
 
165
- BridgeResult sqliteDetachDb(std::string const mainDBName, std::string const alias)
166
- {
167
- /**
168
- * There is no need to check if mainDBName is opened because sqliteExecuteLiteral will do that.
169
- * */
170
- std::string statement = "DETACH DATABASE " + alias;
171
- BridgeResult result = sqliteExecuteLiteral(mainDBName, statement);
172
- if (result.type == SQLiteError)
173
- {
174
- return BridgeResult{
175
- .type = SQLiteError,
176
- .message = mainDBName + "was unable to detach database: " + std::string(result.message),
177
- };
285
+ case SQLITE_FLOAT: {
286
+ double column_value = sqlite3_column_double(statement, i);
287
+ row.values.push_back(JSVariant(column_value));
288
+ break;
178
289
  }
179
- return BridgeResult{
180
- .type = SQLiteOk,
181
- };
182
- }
183
290
 
184
- BridgeResult sqliteRemoveDb(std::string const dbName, std::string const docPath)
185
- {
186
- if (dbMap.count(dbName) == 1)
187
- {
188
- BridgeResult closeResult = sqliteCloseDb(dbName);
189
- if (closeResult.type == SQLiteError)
190
- {
191
- return closeResult;
192
- }
291
+ case SQLITE_TEXT: {
292
+ const char *column_value =
293
+ reinterpret_cast<const char *>(sqlite3_column_text(statement, i));
294
+ int byteLen = sqlite3_column_bytes(statement, i);
295
+ // Specify length too; in case string contains NULL in the middle
296
+ row.values.push_back(JSVariant(std::string(column_value, byteLen)));
297
+ break;
193
298
  }
194
-
195
- std::string dbPath = get_db_path(dbName, docPath);
196
-
197
- if (!file_exists(dbPath))
198
- {
199
- return {
200
- .type = SQLiteError,
201
- .message = "[op-sqlite]: Database file not found" + dbPath
202
- };
299
+
300
+ case SQLITE_BLOB: {
301
+ int blob_size = sqlite3_column_bytes(statement, i);
302
+ const void *blob = sqlite3_column_blob(statement, i);
303
+ uint8_t *data = new uint8_t[blob_size];
304
+ // You cannot share raw memory between native and JS
305
+ // always copy the data
306
+ memcpy(data, blob, blob_size);
307
+ row.values.push_back(
308
+ JSVariant(ArrayBuffer{.data = std::shared_ptr<uint8_t>{data},
309
+ .size = static_cast<size_t>(blob_size)}));
310
+ break;
203
311
  }
204
-
205
- remove(dbPath.c_str());
206
-
207
- return {
208
- .type = SQLiteOk,
209
- };
210
- }
211
312
 
212
- inline void bindStatement(sqlite3_stmt *statement, const std::vector<JSVariant> *values)
213
- {
214
- size_t size = values->size();
215
-
216
- for (int ii = 0; ii < size; ii++)
217
- {
218
- int sqIndex = ii + 1;
219
- JSVariant value = values->at(ii);
220
-
221
- if (std::holds_alternative<bool>(value))
222
- {
223
- sqlite3_bind_int(statement, sqIndex, std::get<bool>(value));
224
- }
225
- else if (std::holds_alternative<int>(value))
226
- {
227
- sqlite3_bind_int(statement, sqIndex, std::get<int>(value));
228
- }
229
- else if (std::holds_alternative<long long>(value))
230
- {
231
- sqlite3_bind_double(statement, sqIndex, std::get<long long>(value));
232
- }
233
- else if (std::holds_alternative<double>(value))
234
- {
235
- sqlite3_bind_double(statement, sqIndex, std::get<double>(value));
236
- }
237
- else if (std::holds_alternative<std::string>(value))
238
- {
239
- std::string str = std::get<std::string>(value);
240
- sqlite3_bind_text(statement, sqIndex, str.c_str(), str.length(), SQLITE_TRANSIENT);
241
- }
242
- else if(std::holds_alternative<ArrayBuffer>(value))
243
- {
244
- ArrayBuffer buffer = std::get<ArrayBuffer>(value);
245
- sqlite3_bind_blob(statement, sqIndex, buffer.data.get(), buffer.size, SQLITE_STATIC);
246
- } else {
247
- sqlite3_bind_null(statement, sqIndex);
248
- }
313
+ case SQLITE_NULL:
314
+ // Intentionally left blank
315
+
316
+ default:
317
+ row.values.push_back(JSVariant(NULL));
318
+ break;
249
319
  }
320
+ i++;
321
+ }
322
+ if (results != nullptr) {
323
+ results->push_back(row);
324
+ }
325
+ break;
250
326
  }
251
327
 
252
- BridgeResult sqliteExecute(std::string const dbName,
253
- std::string const &query,
254
- const std::vector<JSVariant> *params,
255
- std::vector<DumbHostObject> *results,
256
- std::shared_ptr<std::vector<DynamicHostObject>> metadatas)
257
- {
258
-
259
- if (dbMap.find(dbName) == dbMap.end())
260
- {
261
- return {
262
- .type = SQLiteError,
263
- .message = "[op-sqlite]: Database " + dbName + " is not open"
264
- };
328
+ case SQLITE_DONE:
329
+ if (metadatas != nullptr) {
330
+ i = 0;
331
+ count = sqlite3_column_count(statement);
332
+
333
+ while (i < count) {
334
+ column_name = sqlite3_column_name(statement, i);
335
+ const char *type = sqlite3_column_decltype(statement, i);
336
+ auto metadata = SmartHostObject();
337
+ metadata.fields.push_back(std::make_pair("name", column_name));
338
+ metadata.fields.push_back(std::make_pair("index", i));
339
+ metadata.fields.push_back(
340
+ std::make_pair("type", type == NULL ? "UNKNOWN" : type));
341
+
342
+ metadatas->push_back(metadata);
343
+ i++;
265
344
  }
266
-
267
- sqlite3 *db = dbMap[dbName];
268
-
269
- sqlite3_stmt *statement;
270
- const char *errorMessage;
271
- const char *remainingStatement = nullptr;
272
-
273
- bool isConsuming = true;
274
- bool isFailed = false;
275
-
276
- int result = SQLITE_OK;
277
-
278
- do {
279
- const char *queryStr = remainingStatement == nullptr ? query.c_str() : remainingStatement;
280
-
281
- int statementStatus = sqlite3_prepare_v2(db, queryStr, -1, &statement, &remainingStatement);
282
-
283
- if (statementStatus == SQLITE_ERROR)
284
- {
285
- const char *message = sqlite3_errmsg(db);
286
- return {
287
- .type = SQLiteError,
288
- .message = "[op-sqlite] SQL statement error: " + std::string(message),
289
- };
290
- }
291
-
292
- bindStatement(statement, params);
293
-
294
- isConsuming = true;
295
-
296
- int i, count, column_type;
297
- std::string column_name, column_declared_type;
298
-
299
- while (isConsuming)
300
- {
301
- result = sqlite3_step(statement);
302
-
303
- switch (result)
304
- {
305
- case SQLITE_ROW: {
306
- if(results == NULL)
307
- {
308
- break;
309
- }
310
-
311
- i = 0;
312
- DumbHostObject row = DumbHostObject(metadatas);
313
-
314
- count = sqlite3_column_count(statement);
315
-
316
- while (i < count)
317
- {
318
- column_type = sqlite3_column_type(statement, i);
319
-
320
- switch (column_type)
321
- {
322
- case SQLITE_INTEGER:
323
- {
324
- /**
325
- * Warning this will loose precision because JS can
326
- * only represent Integers up to 53 bits
327
- */
328
- double column_value = sqlite3_column_double(statement, i);
329
- row.values.push_back(JSVariant(column_value));
330
- break;
331
- }
332
-
333
- case SQLITE_FLOAT:
334
- {
335
- double column_value = sqlite3_column_double(statement, i);
336
- row.values.push_back(JSVariant(column_value));
337
- break;
338
- }
339
-
340
- case SQLITE_TEXT:
341
- {
342
- const char *column_value = reinterpret_cast<const char *>(sqlite3_column_text(statement, i));
343
- int byteLen = sqlite3_column_bytes(statement, i);
344
- // Specify length too; in case string contains NULL in the middle
345
- row.values.push_back(JSVariant(std::string(column_value, byteLen)));
346
- break;
347
- }
348
-
349
- case SQLITE_BLOB:
350
- {
351
- int blob_size = sqlite3_column_bytes(statement, i);
352
- const void *blob = sqlite3_column_blob(statement, i);
353
- uint8_t *data = new uint8_t[blob_size];
354
- // You cannot share raw memory between native and JS
355
- // always copy the data
356
- memcpy(data, blob, blob_size);
357
- row.values.push_back(JSVariant(ArrayBuffer {
358
- .data = std::shared_ptr<uint8_t>{data},
359
- .size = static_cast<size_t>(blob_size)
360
- }));
361
- break;
362
- }
363
-
364
- case SQLITE_NULL:
365
- // Intentionally left blank
366
-
367
- default:
368
- row.values.push_back(JSVariant(NULL));
369
- break;
370
- }
371
- i++;
372
- }
373
- if (results != nullptr) {
374
- results->push_back(row);
375
- }
376
- break;
377
- }
378
-
379
- case SQLITE_DONE:
380
- if (metadatas != nullptr) {
381
- i = 0;
382
- count = sqlite3_column_count(statement);
383
-
384
- while (i < count)
385
- {
386
- column_name = sqlite3_column_name(statement, i);
387
- const char *type = sqlite3_column_decltype(statement, i);
388
- auto metadata = DynamicHostObject();
389
- metadata.fields.push_back(std::make_pair("name", column_name));
390
- metadata.fields.push_back(std::make_pair("index", i));
391
- metadata.fields.push_back(std::make_pair("type", type == NULL ? "UNKNOWN" : type));
392
-
393
- metadatas->push_back(metadata);
394
- i++;
395
- }
396
- }
397
- isConsuming = false;
398
- break;
399
-
400
- default:
401
- errorMessage = sqlite3_errmsg(db);
402
- isFailed = true;
403
- isConsuming = false;
404
- }
405
- }
406
-
407
- sqlite3_finalize(statement);
408
- } while (remainingStatement != NULL && strcmp(remainingStatement, "") != 0 && !isFailed);
409
-
410
-
411
- if (isFailed)
412
- {
413
-
414
- return {
415
- .type = SQLiteError,
416
- .message = "[op-sqlite] SQLite code: " + std::to_string(result) + " execution error: " + std::string(errorMessage)
417
- };
418
- }
419
-
420
- int changedRowCount = sqlite3_changes(db);
421
- long long latestInsertRowId = sqlite3_last_insert_rowid(db);
422
-
423
- return {
424
- .type = SQLiteOk,
425
- .affectedRows = changedRowCount,
426
- .insertId = static_cast<double>(latestInsertRowId)
427
- };
345
+ }
346
+ isConsuming = false;
347
+ break;
348
+
349
+ default:
350
+ errorMessage = sqlite3_errmsg(db);
351
+ isFailed = true;
352
+ isConsuming = false;
428
353
  }
354
+ }
429
355
 
430
- BridgeResult sqliteExecuteLiteral(std::string const dbName, std::string const &query)
431
- {
432
- if (dbMap.count(dbName) == 0)
433
- {
434
- return {
435
- SQLiteError,
436
- "[op-sqlite] Database not opened: " + dbName
437
- };
438
- }
439
-
440
- sqlite3 *db = dbMap[dbName];
441
- sqlite3_stmt *statement;
442
-
443
- int statementStatus = sqlite3_prepare_v2(db, query.c_str(), -1, &statement, NULL);
444
-
445
- if (statementStatus != SQLITE_OK)
446
- {
447
- const char *message = sqlite3_errmsg(db);
448
- return {
449
- SQLiteError,
450
- "[op-sqlite] SQL statement error: " + std::string(message),
451
- 0};
452
- }
453
-
454
- bool isConsuming = true;
455
- bool isFailed = false;
456
-
457
- int result;
458
- std::string column_name;
459
-
460
- while (isConsuming)
461
- {
462
- result = sqlite3_step(statement);
463
-
464
- switch (result)
465
- {
466
- case SQLITE_ROW:
467
- isConsuming = true;
468
- break;
469
-
470
- case SQLITE_DONE:
471
- isConsuming = false;
472
- break;
473
-
474
- default:
475
- isFailed = true;
476
- isConsuming = false;
477
- }
478
- }
479
-
480
- sqlite3_finalize(statement);
481
-
482
- if (isFailed)
483
- {
484
- const char *message = sqlite3_errmsg(db);
485
- return {
486
- SQLiteError,
487
- "[op-sqlite] SQL execution error: " + std::string(message),
488
- 0};
489
- }
490
-
491
- int changedRowCount = sqlite3_changes(db);
492
- return {
493
- SQLiteOk,
494
- "",
495
- changedRowCount};
356
+ if (isFailed) {
357
+
358
+ return {.type = SQLiteError,
359
+ .message = "[op-sqlite] SQLite code: " + std::to_string(result) +
360
+ " execution error: " + std::string(errorMessage)};
361
+ }
362
+
363
+ int changedRowCount = sqlite3_changes(db);
364
+ long long latestInsertRowId = sqlite3_last_insert_rowid(db);
365
+
366
+ return {.type = SQLiteOk,
367
+ .affectedRows = changedRowCount,
368
+ .insertId = static_cast<double>(latestInsertRowId)};
369
+ }
370
+
371
+ sqlite3_stmt *sqlite_prepare_statement(std::string const dbName,
372
+ std::string const &query) {
373
+ if (dbMap.find(dbName) == dbMap.end()) {
374
+ throw std::runtime_error("Database not opened");
375
+ }
376
+
377
+ sqlite3 *db = dbMap[dbName];
378
+
379
+ sqlite3_stmt *statement;
380
+
381
+ const char *queryStr = query.c_str();
382
+
383
+ int statementStatus = sqlite3_prepare_v2(db, queryStr, -1, &statement, NULL);
384
+
385
+ if (statementStatus == SQLITE_ERROR) {
386
+ const char *message = sqlite3_errmsg(db);
387
+ throw std::runtime_error("[op-sqlite] SQL statement error: " +
388
+ std::string(message));
389
+ }
390
+
391
+ return statement;
392
+ }
393
+
394
+ BridgeResult
395
+ sqliteExecute(std::string const dbName, std::string const &query,
396
+ const std::vector<JSVariant> *params,
397
+ std::vector<DumbHostObject> *results,
398
+ std::shared_ptr<std::vector<SmartHostObject>> metadatas) {
399
+
400
+ if (dbMap.find(dbName) == dbMap.end()) {
401
+ return {.type = SQLiteError,
402
+ .message = "[op-sqlite]: Database " + dbName + " is not open"};
403
+ }
404
+
405
+ sqlite3 *db = dbMap[dbName];
406
+
407
+ sqlite3_stmt *statement;
408
+ const char *errorMessage;
409
+ const char *remainingStatement = nullptr;
410
+
411
+ bool isConsuming = true;
412
+ bool isFailed = false;
413
+
414
+ int result = SQLITE_OK;
415
+
416
+ do {
417
+ const char *queryStr =
418
+ remainingStatement == nullptr ? query.c_str() : remainingStatement;
419
+
420
+ int statementStatus =
421
+ sqlite3_prepare_v2(db, queryStr, -1, &statement, &remainingStatement);
422
+
423
+ if (statementStatus == SQLITE_ERROR) {
424
+ const char *message = sqlite3_errmsg(db);
425
+ return {
426
+ .type = SQLiteError,
427
+ .message = "[op-sqlite] SQL statement error: " + std::string(message),
428
+ };
496
429
  }
497
430
 
498
- void sqliteCloseAll() {
499
- for (auto const& x : dbMap) {
500
- // Interrupt will make all pending operations to fail with SQLITE_INTERRUPT
501
- // The ongoing work from threads will then fail ASAP
502
- sqlite3_interrupt(x.second);
503
- // Each DB connection can then be safely interrupted
504
- sqlite3_close_v2(x.second);
505
- }
506
- dbMap.clear();
431
+ if (params != nullptr && params->size() > 0) {
432
+ bindStatement(statement, params);
507
433
  }
508
434
 
509
- std::string operationToString(int operation_type) {
510
- switch (operation_type) {
511
- case SQLITE_INSERT:
512
- return "INSERT";
513
-
514
- case SQLITE_DELETE:
515
- return "DELETE";
516
-
517
- case SQLITE_UPDATE:
518
- return "UPDATE";
519
-
520
- default:
521
- throw std::invalid_argument("Uknown SQLite operation on hook");
435
+ isConsuming = true;
436
+
437
+ int i, count, column_type;
438
+ std::string column_name, column_declared_type;
439
+
440
+ while (isConsuming) {
441
+ result = sqlite3_step(statement);
442
+
443
+ switch (result) {
444
+ case SQLITE_ROW: {
445
+ if (results == NULL) {
446
+ break;
522
447
  }
523
- }
524
448
 
525
- void update_callback(void *dbName,
526
- int operation_type,
527
- char const *database,
528
- char const *table,
529
- sqlite3_int64 rowid) {
530
- std::string &strDbName = *(static_cast<std::string*>(dbName));
531
- auto callback = updateCallbackMap[strDbName];
532
- callback(strDbName, std::string(table), operationToString(operation_type), static_cast<int>(rowid));
533
- }
534
-
535
- BridgeResult registerUpdateHook(std::string const dbName,
536
- std::function<void (std::string dbName, std::string tableName, std::string operation, int rowId)> const callback) {
537
- if (dbMap.count(dbName) == 0)
538
- {
539
- return {
540
- SQLiteError,
541
- "[op-sqlite] Database not opened: " + dbName
542
- };
449
+ i = 0;
450
+ DumbHostObject row = DumbHostObject(metadatas);
451
+
452
+ count = sqlite3_column_count(statement);
453
+
454
+ while (i < count) {
455
+ column_type = sqlite3_column_type(statement, i);
456
+
457
+ switch (column_type) {
458
+ case SQLITE_INTEGER: {
459
+ /**
460
+ * Warning this will loose precision because JS can
461
+ * only represent Integers up to 53 bits
462
+ */
463
+ double column_value = sqlite3_column_double(statement, i);
464
+ row.values.push_back(JSVariant(column_value));
465
+ break;
466
+ }
467
+
468
+ case SQLITE_FLOAT: {
469
+ double column_value = sqlite3_column_double(statement, i);
470
+ row.values.push_back(JSVariant(column_value));
471
+ break;
472
+ }
473
+
474
+ case SQLITE_TEXT: {
475
+ const char *column_value = reinterpret_cast<const char *>(
476
+ sqlite3_column_text(statement, i));
477
+ int byteLen = sqlite3_column_bytes(statement, i);
478
+ // Specify length too; in case string contains NULL in the middle
479
+ row.values.push_back(JSVariant(std::string(column_value, byteLen)));
480
+ break;
481
+ }
482
+
483
+ case SQLITE_BLOB: {
484
+ int blob_size = sqlite3_column_bytes(statement, i);
485
+ const void *blob = sqlite3_column_blob(statement, i);
486
+ uint8_t *data = new uint8_t[blob_size];
487
+ // You cannot share raw memory between native and JS
488
+ // always copy the data
489
+ memcpy(data, blob, blob_size);
490
+ row.values.push_back(
491
+ JSVariant(ArrayBuffer{.data = std::shared_ptr<uint8_t>{data},
492
+ .size = static_cast<size_t>(blob_size)}));
493
+ break;
494
+ }
495
+
496
+ case SQLITE_NULL:
497
+ // Intentionally left blank
498
+
499
+ default:
500
+ row.values.push_back(JSVariant(NULL));
501
+ break;
502
+ }
503
+ i++;
543
504
  }
544
-
545
- sqlite3 *db = dbMap[dbName];
546
- updateCallbackMap[dbName] = callback;
547
- const std::string *key = nullptr;
548
-
549
- // TODO find a more elegant way to retrieve a reference to the key
550
- for (auto const& element : dbMap) {
551
- if(element.first == dbName) {
552
- key = &element.first;
553
- }
505
+ if (results != nullptr) {
506
+ results->push_back(row);
554
507
  }
555
-
556
- sqlite3_update_hook(
557
- db,
558
- &update_callback,
559
- (void *)key);
560
-
561
- return {
562
- SQLiteOk
563
- };
564
- }
565
-
566
- BridgeResult unregisterUpdateHook(std::string const dbName) {
567
- if (dbMap.count(dbName) == 0)
568
- {
569
- return {
570
- SQLiteError,
571
- "[op-sqlite] Database not opened: " + dbName
572
- };
508
+ break;
509
+ }
510
+
511
+ case SQLITE_DONE:
512
+ if (metadatas != nullptr) {
513
+ i = 0;
514
+ count = sqlite3_column_count(statement);
515
+
516
+ while (i < count) {
517
+ column_name = sqlite3_column_name(statement, i);
518
+ const char *type = sqlite3_column_decltype(statement, i);
519
+ auto metadata = SmartHostObject();
520
+ metadata.fields.push_back(std::make_pair("name", column_name));
521
+ metadata.fields.push_back(std::make_pair("index", i));
522
+ metadata.fields.push_back(
523
+ std::make_pair("type", type == NULL ? "UNKNOWN" : type));
524
+
525
+ metadatas->push_back(metadata);
526
+ i++;
527
+ }
573
528
  }
574
-
575
- sqlite3 *db = dbMap[dbName];
576
- updateCallbackMap.erase(dbName);
577
-
578
- sqlite3_update_hook(
579
- db,
580
- NULL,
581
- NULL);
582
-
583
- return {
584
- SQLiteOk
585
- };
529
+ isConsuming = false;
530
+ break;
531
+
532
+ default:
533
+ errorMessage = sqlite3_errmsg(db);
534
+ isFailed = true;
535
+ isConsuming = false;
536
+ }
586
537
  }
587
538
 
588
- int commit_callback(void *dbName) {
589
- std::string &strDbName = *(static_cast<std::string*>(dbName));
590
- auto callback = commitCallbackMap[strDbName];
591
- callback(strDbName);
592
- // You need to return 0 to allow commits to continue
593
- return 0;
594
- }
539
+ sqlite3_finalize(statement);
540
+ } while (remainingStatement != NULL && strcmp(remainingStatement, "") != 0 &&
541
+ !isFailed);
595
542
 
596
- BridgeResult registerCommitHook(std::string const dbName,
597
- std::function<void (std::string dbName)> const callback) {
598
- if (dbMap.count(dbName) == 0)
599
- {
600
- return {
601
- SQLiteError,
602
- "[op-sqlite] Database not opened: " + dbName
603
- };
604
- }
605
-
606
- sqlite3 *db = dbMap[dbName];
607
- commitCallbackMap[dbName] = callback;
608
- const std::string *key = nullptr;
609
-
610
- // TODO find a more elegant way to retrieve a reference to the key
611
- for (auto const& element : dbMap) {
612
- if(element.first == dbName) {
613
- key = &element.first;
614
- }
615
- }
616
-
617
- sqlite3_commit_hook(
618
- db,
619
- &commit_callback,
620
- (void *)key);
621
-
622
- return {
623
- SQLiteOk
624
- };
625
- }
543
+ if (isFailed) {
626
544
 
627
- BridgeResult unregisterCommitHook(std::string const dbName) {
628
- if (dbMap.count(dbName) == 0)
629
- {
630
- return {
631
- SQLiteError,
632
- "[op-sqlite] Database not opened: " + dbName
633
- };
634
- }
635
-
636
- sqlite3 *db = dbMap[dbName];
637
- commitCallbackMap.erase(dbName);
638
- sqlite3_commit_hook(
639
- db,
640
- NULL,
641
- NULL);
642
-
643
- return {
644
- SQLiteOk
645
- };
545
+ return {.type = SQLiteError,
546
+ .message = "[op-sqlite] SQLite code: " + std::to_string(result) +
547
+ " execution error: " + std::string(errorMessage)};
548
+ }
549
+
550
+ int changedRowCount = sqlite3_changes(db);
551
+ long long latestInsertRowId = sqlite3_last_insert_rowid(db);
552
+
553
+ return {.type = SQLiteOk,
554
+ .affectedRows = changedRowCount,
555
+ .insertId = static_cast<double>(latestInsertRowId)};
556
+ }
557
+
558
+ BridgeResult sqliteExecuteLiteral(std::string const dbName,
559
+ std::string const &query) {
560
+ if (dbMap.count(dbName) == 0) {
561
+ return {SQLiteError, "[op-sqlite] Database not opened: " + dbName};
562
+ }
563
+
564
+ sqlite3 *db = dbMap[dbName];
565
+ sqlite3_stmt *statement;
566
+
567
+ int statementStatus =
568
+ sqlite3_prepare_v2(db, query.c_str(), -1, &statement, NULL);
569
+
570
+ if (statementStatus != SQLITE_OK) {
571
+ const char *message = sqlite3_errmsg(db);
572
+ return {SQLiteError,
573
+ "[op-sqlite] SQL statement error: " + std::string(message), 0};
574
+ }
575
+
576
+ bool isConsuming = true;
577
+ bool isFailed = false;
578
+
579
+ int result;
580
+ std::string column_name;
581
+
582
+ while (isConsuming) {
583
+ result = sqlite3_step(statement);
584
+
585
+ switch (result) {
586
+ case SQLITE_ROW:
587
+ isConsuming = true;
588
+ break;
589
+
590
+ case SQLITE_DONE:
591
+ isConsuming = false;
592
+ break;
593
+
594
+ default:
595
+ isFailed = true;
596
+ isConsuming = false;
646
597
  }
598
+ }
599
+
600
+ sqlite3_finalize(statement);
647
601
 
648
- void rollback_callback(void *dbName) {
649
- std::string &strDbName = *(static_cast<std::string*>(dbName));
650
- auto callback = rollbackCallbackMap[strDbName];
651
- callback(strDbName);
602
+ if (isFailed) {
603
+ const char *message = sqlite3_errmsg(db);
604
+ return {SQLiteError,
605
+ "[op-sqlite] SQL execution error: " + std::string(message), 0};
606
+ }
607
+
608
+ int changedRowCount = sqlite3_changes(db);
609
+ return {SQLiteOk, "", changedRowCount};
610
+ }
611
+
612
+ void sqliteCloseAll() {
613
+ for (auto const &x : dbMap) {
614
+ // Interrupt will make all pending operations to fail with SQLITE_INTERRUPT
615
+ // The ongoing work from threads will then fail ASAP
616
+ sqlite3_interrupt(x.second);
617
+ // Each DB connection can then be safely interrupted
618
+ sqlite3_close_v2(x.second);
619
+ }
620
+ dbMap.clear();
621
+ }
622
+
623
+ std::string operationToString(int operation_type) {
624
+ switch (operation_type) {
625
+ case SQLITE_INSERT:
626
+ return "INSERT";
627
+
628
+ case SQLITE_DELETE:
629
+ return "DELETE";
630
+
631
+ case SQLITE_UPDATE:
632
+ return "UPDATE";
633
+
634
+ default:
635
+ throw std::invalid_argument("Uknown SQLite operation on hook");
636
+ }
637
+ }
638
+
639
+ void update_callback(void *dbName, int operation_type, char const *database,
640
+ char const *table, sqlite3_int64 rowid) {
641
+ std::string &strDbName = *(static_cast<std::string *>(dbName));
642
+ auto callback = updateCallbackMap[strDbName];
643
+ callback(strDbName, std::string(table), operationToString(operation_type),
644
+ static_cast<int>(rowid));
645
+ }
646
+
647
+ BridgeResult registerUpdateHook(
648
+ std::string const dbName,
649
+ std::function<void(std::string dbName, std::string tableName,
650
+ std::string operation, int rowId)> const callback) {
651
+ if (dbMap.count(dbName) == 0) {
652
+ return {SQLiteError, "[op-sqlite] Database not opened: " + dbName};
653
+ }
654
+
655
+ sqlite3 *db = dbMap[dbName];
656
+ updateCallbackMap[dbName] = callback;
657
+ const std::string *key = nullptr;
658
+
659
+ // TODO find a more elegant way to retrieve a reference to the key
660
+ for (auto const &element : dbMap) {
661
+ if (element.first == dbName) {
662
+ key = &element.first;
652
663
  }
664
+ }
653
665
 
654
- BridgeResult registerRollbackHook(std::string const dbName,
655
- std::function<void (std::string dbName)> const callback) {
656
- if (dbMap.count(dbName) == 0)
657
- {
658
- return {
659
- SQLiteError,
660
- "[op-sqlite] Database not opened: " + dbName
661
- };
662
- }
663
-
664
- sqlite3 *db = dbMap[dbName];
665
- rollbackCallbackMap[dbName] = callback;
666
- const std::string *key = nullptr;
667
-
668
- // TODO find a more elegant way to retrieve a reference to the key
669
- for (auto const& element : dbMap) {
670
- if(element.first == dbName) {
671
- key = &element.first;
672
- }
673
- }
674
-
675
- sqlite3_rollback_hook(
676
- db,
677
- &rollback_callback,
678
- (void *)key);
679
-
680
- return {
681
- SQLiteOk
682
- };
666
+ sqlite3_update_hook(db, &update_callback, (void *)key);
667
+
668
+ return {SQLiteOk};
669
+ }
670
+
671
+ BridgeResult unregisterUpdateHook(std::string const dbName) {
672
+ if (dbMap.count(dbName) == 0) {
673
+ return {SQLiteError, "[op-sqlite] Database not opened: " + dbName};
674
+ }
675
+
676
+ sqlite3 *db = dbMap[dbName];
677
+ updateCallbackMap.erase(dbName);
678
+
679
+ sqlite3_update_hook(db, NULL, NULL);
680
+
681
+ return {SQLiteOk};
682
+ }
683
+
684
+ int commit_callback(void *dbName) {
685
+ std::string &strDbName = *(static_cast<std::string *>(dbName));
686
+ auto callback = commitCallbackMap[strDbName];
687
+ callback(strDbName);
688
+ // You need to return 0 to allow commits to continue
689
+ return 0;
690
+ }
691
+
692
+ BridgeResult
693
+ registerCommitHook(std::string const dbName,
694
+ std::function<void(std::string dbName)> const callback) {
695
+ if (dbMap.count(dbName) == 0) {
696
+ return {SQLiteError, "[op-sqlite] Database not opened: " + dbName};
697
+ }
698
+
699
+ sqlite3 *db = dbMap[dbName];
700
+ commitCallbackMap[dbName] = callback;
701
+ const std::string *key = nullptr;
702
+
703
+ // TODO find a more elegant way to retrieve a reference to the key
704
+ for (auto const &element : dbMap) {
705
+ if (element.first == dbName) {
706
+ key = &element.first;
683
707
  }
708
+ }
684
709
 
685
- BridgeResult unregisterRollbackHook(std::string const dbName) {
686
- if (dbMap.count(dbName) == 0)
687
- {
688
- return {
689
- SQLiteError,
690
- "[op-sqlite] Database not opened: " + dbName
691
- };
692
- }
693
-
694
- sqlite3 *db = dbMap[dbName];
695
- rollbackCallbackMap.erase(dbName);
696
-
697
- sqlite3_rollback_hook(
698
- db,
699
- NULL,
700
- NULL);
701
-
702
- return {
703
- SQLiteOk
704
- };
710
+ sqlite3_commit_hook(db, &commit_callback, (void *)key);
711
+
712
+ return {SQLiteOk};
713
+ }
714
+
715
+ BridgeResult unregisterCommitHook(std::string const dbName) {
716
+ if (dbMap.count(dbName) == 0) {
717
+ return {SQLiteError, "[op-sqlite] Database not opened: " + dbName};
718
+ }
719
+
720
+ sqlite3 *db = dbMap[dbName];
721
+ commitCallbackMap.erase(dbName);
722
+ sqlite3_commit_hook(db, NULL, NULL);
723
+
724
+ return {SQLiteOk};
725
+ }
726
+
727
+ void rollback_callback(void *dbName) {
728
+ std::string &strDbName = *(static_cast<std::string *>(dbName));
729
+ auto callback = rollbackCallbackMap[strDbName];
730
+ callback(strDbName);
731
+ }
732
+
733
+ BridgeResult
734
+ registerRollbackHook(std::string const dbName,
735
+ std::function<void(std::string dbName)> const callback) {
736
+ if (dbMap.count(dbName) == 0) {
737
+ return {SQLiteError, "[op-sqlite] Database not opened: " + dbName};
738
+ }
739
+
740
+ sqlite3 *db = dbMap[dbName];
741
+ rollbackCallbackMap[dbName] = callback;
742
+ const std::string *key = nullptr;
743
+
744
+ // TODO find a more elegant way to retrieve a reference to the key
745
+ for (auto const &element : dbMap) {
746
+ if (element.first == dbName) {
747
+ key = &element.first;
705
748
  }
749
+ }
750
+
751
+ sqlite3_rollback_hook(db, &rollback_callback, (void *)key);
752
+
753
+ return {SQLiteOk};
754
+ }
755
+
756
+ BridgeResult unregisterRollbackHook(std::string const dbName) {
757
+ if (dbMap.count(dbName) == 0) {
758
+ return {SQLiteError, "[op-sqlite] Database not opened: " + dbName};
759
+ }
760
+
761
+ sqlite3 *db = dbMap[dbName];
762
+ rollbackCallbackMap.erase(dbName);
763
+
764
+ sqlite3_rollback_hook(db, NULL, NULL);
765
+
766
+ return {SQLiteOk};
706
767
  }
768
+ } // namespace opsqlite