react-native-nitro-sqlite 9.8.1 → 10.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.
Files changed (145) hide show
  1. package/RNNitroSQLite.podspec +7 -3
  2. package/android/CMakeLists.txt +3 -2
  3. package/android/build.gradle +2 -0
  4. package/android/sqlite-flags.gradle +47 -0
  5. package/cpp/NitroSQLiteDatabaseConnections.cpp +306 -0
  6. package/cpp/NitroSQLiteDatabaseConnections.hpp +86 -0
  7. package/cpp/{databaseMigration.cpp → NitroSQLiteDatabaseMigration.cpp} +2 -2
  8. package/cpp/{sqliteExecuteBatch.cpp → NitroSQLiteExecuteBatch.cpp} +4 -4
  9. package/cpp/{sqliteExecuteBatch.hpp → NitroSQLiteExecuteBatch.hpp} +3 -6
  10. package/cpp/{importSqlFile.cpp → NitroSQLiteImportSqlFile.cpp} +4 -4
  11. package/cpp/{importSqlFile.hpp → NitroSQLiteImportSqlFile.hpp} +3 -3
  12. package/cpp/{macros.hpp → NitroSQLiteMacros.hpp} +3 -3
  13. package/cpp/NitroSQLiteOperations.cpp +396 -0
  14. package/cpp/NitroSQLiteOperations.hpp +72 -0
  15. package/cpp/{types.hpp → NitroSQLiteTypes.hpp} +3 -6
  16. package/cpp/{utils.hpp → NitroSQLiteUtils.hpp} +2 -2
  17. package/cpp/hybridObjects/HybridNitroSQLite.cpp +108 -22
  18. package/cpp/hybridObjects/HybridNitroSQLite.hpp +49 -5
  19. package/cpp/hybridObjects/HybridNitroSQLitePreparedStatement.cpp +59 -0
  20. package/cpp/hybridObjects/HybridNitroSQLitePreparedStatement.hpp +33 -0
  21. package/cpp/hybridObjects/HybridNitroSQLiteQueryResult.hpp +6 -4
  22. package/ios/OnLoad.mm +38 -2
  23. package/lib/commonjs/DatabaseQueue.js +52 -16
  24. package/lib/commonjs/DatabaseQueue.js.map +1 -1
  25. package/lib/commonjs/NitroSQLiteError.js +6 -7
  26. package/lib/commonjs/NitroSQLiteError.js.map +1 -1
  27. package/lib/commonjs/OnLoad.js +1 -1
  28. package/lib/commonjs/index.js +8 -3
  29. package/lib/commonjs/index.js.map +1 -1
  30. package/lib/commonjs/operations/execute.js +19 -4
  31. package/lib/commonjs/operations/execute.js.map +1 -1
  32. package/lib/commonjs/operations/executeBatch.js +19 -6
  33. package/lib/commonjs/operations/executeBatch.js.map +1 -1
  34. package/lib/commonjs/operations/prepare.js +54 -0
  35. package/lib/commonjs/operations/prepare.js.map +1 -0
  36. package/lib/commonjs/operations/session.js +106 -30
  37. package/lib/commonjs/operations/session.js.map +1 -1
  38. package/lib/commonjs/operations/transaction.js +30 -5
  39. package/lib/commonjs/operations/transaction.js.map +1 -1
  40. package/lib/commonjs/specs/NitroSQLitePreparedStatement.nitro.js +6 -0
  41. package/lib/commonjs/specs/NitroSQLitePreparedStatement.nitro.js.map +1 -0
  42. package/lib/commonjs/typeORM.js +10 -3
  43. package/lib/commonjs/typeORM.js.map +1 -1
  44. package/lib/commonjs/types.js +35 -9
  45. package/lib/commonjs/types.js.map +1 -1
  46. package/lib/module/DatabaseQueue.js +52 -16
  47. package/lib/module/DatabaseQueue.js.map +1 -1
  48. package/lib/module/NitroSQLiteError.js +6 -7
  49. package/lib/module/NitroSQLiteError.js.map +1 -1
  50. package/lib/module/OnLoad.js +1 -1
  51. package/lib/module/index.js +8 -3
  52. package/lib/module/index.js.map +1 -1
  53. package/lib/module/operations/execute.js +20 -5
  54. package/lib/module/operations/execute.js.map +1 -1
  55. package/lib/module/operations/executeBatch.js +20 -7
  56. package/lib/module/operations/executeBatch.js.map +1 -1
  57. package/lib/module/operations/prepare.js +49 -0
  58. package/lib/module/operations/prepare.js.map +1 -0
  59. package/lib/module/operations/session.js +107 -31
  60. package/lib/module/operations/session.js.map +1 -1
  61. package/lib/module/operations/transaction.js +30 -5
  62. package/lib/module/operations/transaction.js.map +1 -1
  63. package/lib/module/specs/NitroSQLitePreparedStatement.nitro.js +4 -0
  64. package/lib/module/specs/NitroSQLitePreparedStatement.nitro.js.map +1 -0
  65. package/lib/module/typeORM.js +11 -3
  66. package/lib/module/typeORM.js.map +1 -1
  67. package/lib/module/types.js +48 -9
  68. package/lib/module/types.js.map +1 -1
  69. package/lib/typescript/commonjs/DatabaseQueue.d.ts +15 -7
  70. package/lib/typescript/commonjs/DatabaseQueue.d.ts.map +1 -1
  71. package/lib/typescript/commonjs/NitroSQLiteError.d.ts +6 -7
  72. package/lib/typescript/commonjs/NitroSQLiteError.d.ts.map +1 -1
  73. package/lib/typescript/commonjs/index.d.ts +9 -2
  74. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  75. package/lib/typescript/commonjs/operations/execute.d.ts +17 -2
  76. package/lib/typescript/commonjs/operations/execute.d.ts.map +1 -1
  77. package/lib/typescript/commonjs/operations/executeBatch.d.ts +15 -2
  78. package/lib/typescript/commonjs/operations/executeBatch.d.ts.map +1 -1
  79. package/lib/typescript/commonjs/operations/prepare.d.ts +13 -0
  80. package/lib/typescript/commonjs/operations/prepare.d.ts.map +1 -0
  81. package/lib/typescript/commonjs/operations/session.d.ts +8 -0
  82. package/lib/typescript/commonjs/operations/session.d.ts.map +1 -1
  83. package/lib/typescript/commonjs/operations/transaction.d.ts +11 -1
  84. package/lib/typescript/commonjs/operations/transaction.d.ts.map +1 -1
  85. package/lib/typescript/commonjs/specs/NitroSQLite.nitro.d.ts +83 -2
  86. package/lib/typescript/commonjs/specs/NitroSQLite.nitro.d.ts.map +1 -1
  87. package/lib/typescript/commonjs/specs/NitroSQLitePreparedStatement.nitro.d.ts +26 -0
  88. package/lib/typescript/commonjs/specs/NitroSQLitePreparedStatement.nitro.d.ts.map +1 -0
  89. package/lib/typescript/commonjs/specs/NitroSQLiteQueryResult.nitro.d.ts +9 -15
  90. package/lib/typescript/commonjs/specs/NitroSQLiteQueryResult.nitro.d.ts.map +1 -1
  91. package/lib/typescript/commonjs/typeORM.d.ts +14 -3
  92. package/lib/typescript/commonjs/typeORM.d.ts.map +1 -1
  93. package/lib/typescript/commonjs/types.d.ts +124 -21
  94. package/lib/typescript/commonjs/types.d.ts.map +1 -1
  95. package/lib/typescript/module/DatabaseQueue.d.ts +15 -7
  96. package/lib/typescript/module/DatabaseQueue.d.ts.map +1 -1
  97. package/lib/typescript/module/NitroSQLiteError.d.ts +6 -7
  98. package/lib/typescript/module/NitroSQLiteError.d.ts.map +1 -1
  99. package/lib/typescript/module/index.d.ts +9 -2
  100. package/lib/typescript/module/index.d.ts.map +1 -1
  101. package/lib/typescript/module/operations/execute.d.ts +17 -2
  102. package/lib/typescript/module/operations/execute.d.ts.map +1 -1
  103. package/lib/typescript/module/operations/executeBatch.d.ts +15 -2
  104. package/lib/typescript/module/operations/executeBatch.d.ts.map +1 -1
  105. package/lib/typescript/module/operations/prepare.d.ts +13 -0
  106. package/lib/typescript/module/operations/prepare.d.ts.map +1 -0
  107. package/lib/typescript/module/operations/session.d.ts +8 -0
  108. package/lib/typescript/module/operations/session.d.ts.map +1 -1
  109. package/lib/typescript/module/operations/transaction.d.ts +11 -1
  110. package/lib/typescript/module/operations/transaction.d.ts.map +1 -1
  111. package/lib/typescript/module/specs/NitroSQLite.nitro.d.ts +83 -2
  112. package/lib/typescript/module/specs/NitroSQLite.nitro.d.ts.map +1 -1
  113. package/lib/typescript/module/specs/NitroSQLitePreparedStatement.nitro.d.ts +26 -0
  114. package/lib/typescript/module/specs/NitroSQLitePreparedStatement.nitro.d.ts.map +1 -0
  115. package/lib/typescript/module/specs/NitroSQLiteQueryResult.nitro.d.ts +9 -15
  116. package/lib/typescript/module/specs/NitroSQLiteQueryResult.nitro.d.ts.map +1 -1
  117. package/lib/typescript/module/typeORM.d.ts +14 -3
  118. package/lib/typescript/module/typeORM.d.ts.map +1 -1
  119. package/lib/typescript/module/types.d.ts +124 -21
  120. package/lib/typescript/module/types.d.ts.map +1 -1
  121. package/nitrogen/generated/android/RNNitroSQLite+autolinking.cmake +1 -0
  122. package/nitrogen/generated/shared/c++/HybridNitroSQLitePreparedStatementSpec.cpp +24 -0
  123. package/nitrogen/generated/shared/c++/HybridNitroSQLitePreparedStatementSpec.hpp +73 -0
  124. package/nitrogen/generated/shared/c++/HybridNitroSQLiteSpec.cpp +3 -0
  125. package/nitrogen/generated/shared/c++/HybridNitroSQLiteSpec.hpp +8 -2
  126. package/package.json +4 -2
  127. package/src/DatabaseQueue.ts +81 -25
  128. package/src/NitroSQLiteError.ts +6 -7
  129. package/src/OnLoad.ts +1 -1
  130. package/src/index.ts +7 -3
  131. package/src/operations/execute.ts +22 -3
  132. package/src/operations/executeBatch.ts +20 -5
  133. package/src/operations/prepare.ts +69 -0
  134. package/src/operations/session.ts +136 -45
  135. package/src/operations/transaction.ts +37 -4
  136. package/src/specs/NitroSQLite.nitro.ts +84 -2
  137. package/src/specs/NitroSQLitePreparedStatement.nitro.ts +28 -0
  138. package/src/specs/NitroSQLiteQueryResult.nitro.ts +9 -15
  139. package/src/typeORM.ts +14 -3
  140. package/src/types.ts +135 -21
  141. package/cpp/operations.cpp +0 -358
  142. package/cpp/operations.hpp +0 -56
  143. /package/android/{cpp-adapter.cpp → RNNitroSQLiteAdapter.cpp} +0 -0
  144. /package/cpp/{databaseMigration.hpp → NitroSQLiteDatabaseMigration.hpp} +0 -0
  145. /package/cpp/{logs.hpp → NitroSQLiteLogs.hpp} +0 -0
@@ -0,0 +1,396 @@
1
+ #include "NitroSQLiteOperations.hpp"
2
+ #include "NitroSQLiteException.hpp"
3
+ #include "NitroSQLiteLogs.hpp"
4
+ #include "NitroSQLiteUtils.hpp"
5
+ #include "hybridObjects/HybridNitroSQLiteQueryResult.hpp"
6
+ #include <NitroModules/ArrayBuffer.hpp>
7
+ #include <NitroModules/Promise.hpp>
8
+ #include <cmath>
9
+ #include <ctime>
10
+ #include <exception>
11
+ #include <iostream>
12
+ #include <limits>
13
+ #include <memory>
14
+ #include <mutex>
15
+ #include <optional>
16
+ #include <sqlite3.h>
17
+ #include <sstream>
18
+ #include <unistd.h>
19
+
20
+ #ifdef NITRO_SQLITE_VEC
21
+ // Angle-bracket so it resolves via -I (CocoaPods intercepts quoted includes).
22
+ #include <NitroSQLiteVecRegisterVectorExtensions.hpp>
23
+ #endif
24
+
25
+ using namespace facebook;
26
+
27
+ namespace margelo::nitro::rnnitrosqlite {
28
+
29
+ static constexpr double kInt64MinAsDouble = static_cast<double>(std::numeric_limits<int64_t>::min());
30
+ static constexpr double kInt64UpperBoundAsDouble = -kInt64MinAsDouble;
31
+
32
+ void SQLiteConnection::enqueueAsync(std::function<void()> operation) {
33
+ std::lock_guard lock(asyncQueueMutex);
34
+ if (!asyncWorkerRunning) {
35
+ Promise<void>::async([connection = shared_from_this()] { connection->drainAsync(); });
36
+ asyncWorkerRunning = true;
37
+ }
38
+ asyncQueue.push(std::move(operation));
39
+ }
40
+
41
+ void SQLiteConnection::drainAsync() {
42
+ while (true) {
43
+ std::function<void()> operation;
44
+ {
45
+ std::lock_guard lock(asyncQueueMutex);
46
+ if (asyncQueue.empty()) {
47
+ asyncWorkerRunning = false;
48
+ return;
49
+ }
50
+ operation = std::move(asyncQueue.front());
51
+ asyncQueue.pop();
52
+ }
53
+ try {
54
+ operation();
55
+ } catch (const std::exception& error) {
56
+ LOGE("Async operation on database %s failed while settling its promise: %s", name.c_str(), error.what());
57
+ } catch (...) {
58
+ LOGE("Async operation on database %s failed while settling its promise", name.c_str());
59
+ }
60
+ }
61
+ }
62
+
63
+ void sqliteOpenDb(const std::string& dbName, const std::string& docPath, bool readOnly) {
64
+ #ifdef NITRO_SQLITE_VEC
65
+ // Register before opening so the connection exposes vec0 + vec_*.
66
+ margelo::rnnitrosqlitevec::registerVectorExtensions();
67
+ #endif
68
+ const std::string dbPath = readOnly ? docPath + "/" + dbName : get_db_path(dbName, docPath);
69
+ databaseConnections().open(dbName, dbPath, readOnly);
70
+ }
71
+
72
+ std::string sqliteOpenConnection(const std::string& dbName, const std::string& docPath, bool readOnly) {
73
+ if (sqlite3_threadsafe() == 0) {
74
+ throw NitroSQLiteException(NitroSQLiteExceptionType::DatabaseCannotBeOpened,
75
+ "Independent connections require a thread-safe SQLite build");
76
+ }
77
+ #ifdef NITRO_SQLITE_VEC
78
+ margelo::rnnitrosqlitevec::registerVectorExtensions();
79
+ #endif
80
+ const std::string dbPath = readOnly ? docPath + "/" + dbName : get_db_path(dbName, docPath);
81
+ return databaseConnections().openIndependent(dbPath, readOnly);
82
+ }
83
+
84
+ void sqliteCloseDb(const std::string& dbName) {
85
+ databaseConnections().close(dbName);
86
+ }
87
+
88
+ void sqliteCloseAll() {
89
+ databaseConnections().closeAll();
90
+ }
91
+
92
+ void sqliteAttachDb(const std::string& mainDBName, const std::string& docPath, const std::string& databaseToAttach,
93
+ const std::string& alias) {
94
+ /**
95
+ * There is no need to check if mainDBName is opened because sqliteExecuteCommand will do that.
96
+ * */
97
+ std::string dbPath = get_db_path(databaseToAttach, docPath);
98
+ std::string statement = "ATTACH DATABASE '" + dbPath + "' AS " + alias;
99
+
100
+ try {
101
+ sqliteExecuteCommand(mainDBName, statement);
102
+ } catch (NitroSQLiteException& e) {
103
+ throw NitroSQLiteException(NitroSQLiteExceptionType::UnableToAttachToDatabase,
104
+ mainDBName + " was unable to attach another database: " + std::string(e.what()));
105
+ }
106
+ }
107
+
108
+ void sqliteDetachDb(const std::string& mainDBName, const std::string& alias) {
109
+ /**
110
+ * There is no need to check if mainDBName is opened because sqliteExecuteCommand will do that.
111
+ * */
112
+ std::string statement = "DETACH DATABASE " + alias;
113
+
114
+ try {
115
+ sqliteExecuteCommand(mainDBName, statement);
116
+ } catch (NitroSQLiteException& e) {
117
+ throw NitroSQLiteException(NitroSQLiteExceptionType::UnableToAttachToDatabase,
118
+ mainDBName + " was unable to detach database: " + std::string(e.what()));
119
+ }
120
+ }
121
+
122
+ void sqliteRemoveDb(const std::string& dbName, const std::string& docPath, const std::optional<std::string>& connectionId,
123
+ const std::optional<std::string>& otherDocPath) {
124
+ std::optional<std::filesystem::path> otherPath;
125
+ if (otherDocPath) {
126
+ otherPath = std::filesystem::path(*otherDocPath) / dbName;
127
+ }
128
+ databaseConnections().drop(dbName, std::filesystem::path(docPath) / dbName, connectionId, otherPath);
129
+ }
130
+
131
+ void bindStatement(sqlite3_stmt* statement, const SQLiteQueryParams& values) {
132
+ for (size_t valueIndex = 0; valueIndex < values.size(); valueIndex++) {
133
+ int sqliteIndex = valueIndex + 1;
134
+ const SQLiteValue& value = values.at(valueIndex);
135
+ int bindStatus = SQLITE_OK;
136
+
137
+ if (std::holds_alternative<NullType>(value)) {
138
+ bindStatus = sqlite3_bind_null(statement, sqliteIndex);
139
+ } else if (std::holds_alternative<bool>(value)) {
140
+ bindStatus = sqlite3_bind_int(statement, sqliteIndex, std::get<bool>(value));
141
+ } else if (std::holds_alternative<double>(value)) {
142
+ // Bind whole numbers as INTEGER so vec0 rowid/pk/partition (which reject REAL) work; SQLite still coerces to REAL for REAL columns.
143
+ double doubleValue = std::get<double>(value);
144
+ if (std::trunc(doubleValue) == doubleValue && doubleValue >= kInt64MinAsDouble && doubleValue < kInt64UpperBoundAsDouble) {
145
+ bindStatus = sqlite3_bind_int64(statement, sqliteIndex, static_cast<sqlite3_int64>(doubleValue));
146
+ } else {
147
+ bindStatus = sqlite3_bind_double(statement, sqliteIndex, doubleValue);
148
+ }
149
+ } else if (std::holds_alternative<std::string>(value)) {
150
+ const auto& stringValue = std::get<std::string>(value);
151
+ bindStatus = sqlite3_bind_text(statement, sqliteIndex, stringValue.c_str(), stringValue.length(), SQLITE_TRANSIENT);
152
+ } else if (std::holds_alternative<std::shared_ptr<ArrayBuffer>>(value)) {
153
+ const auto& arrayBufferValue = std::get<std::shared_ptr<ArrayBuffer>>(value);
154
+ bindStatus = sqlite3_bind_blob(statement, sqliteIndex, arrayBufferValue->data(), arrayBufferValue->size(), SQLITE_TRANSIENT);
155
+ }
156
+
157
+ if (bindStatus != SQLITE_OK) {
158
+ throw NitroSQLiteException::SqlExecution(sqlite3_errmsg(sqlite3_db_handle(statement)));
159
+ }
160
+ }
161
+ }
162
+
163
+ namespace {
164
+
165
+ struct SQLiteStatementFinalizer {
166
+ void operator()(sqlite3_stmt* statement) const noexcept {
167
+ if (statement != nullptr) {
168
+ sqlite3_finalize(statement);
169
+ }
170
+ }
171
+ };
172
+
173
+ using SQLiteStatement = std::unique_ptr<sqlite3_stmt, SQLiteStatementFinalizer>;
174
+
175
+ SQLiteStatement prepareStatement(sqlite3* db, const std::string& query, const std::optional<SQLiteQueryParams>& params) {
176
+ sqlite3_stmt* rawStatement = nullptr;
177
+ int statementStatus = sqlite3_prepare_v2(db, query.c_str(), -1, &rawStatement, nullptr);
178
+ SQLiteStatement statement(rawStatement);
179
+
180
+ if (statementStatus != SQLITE_OK) {
181
+ throw NitroSQLiteException::SqlExecution(sqlite3_errmsg(db));
182
+ }
183
+
184
+ // sqlite3_prepare_v2 reports SQLITE_OK with a null statement when the query holds no SQL,
185
+ // such as an empty string or nothing but comments.
186
+ if (!statement) {
187
+ throw NitroSQLiteException::SqlExecution("Query does not contain any SQL statement");
188
+ }
189
+
190
+ if (params) {
191
+ bindStatement(statement.get(), *params);
192
+ }
193
+
194
+ return statement;
195
+ }
196
+
197
+ template <typename OnRow>
198
+ void consumeStatement(sqlite3* db, sqlite3_stmt* statement, OnRow&& onRow) {
199
+ while (true) {
200
+ int result = sqlite3_step(statement);
201
+
202
+ if (result == SQLITE_ROW) {
203
+ onRow(statement);
204
+ continue;
205
+ }
206
+
207
+ if (result == SQLITE_DONE) {
208
+ return;
209
+ }
210
+
211
+ throw NitroSQLiteException::SqlExecution(sqlite3_errmsg(db));
212
+ }
213
+ }
214
+
215
+ std::shared_ptr<HybridNitroSQLiteQueryResult> executeStatement(sqlite3* db, sqlite3_stmt* statement) {
216
+ SQLiteQueryResults results;
217
+
218
+ consumeStatement(db, statement, [&](sqlite3_stmt* currentStatement) {
219
+ SQLiteQueryResultRow row;
220
+ int count = sqlite3_column_count(currentStatement);
221
+
222
+ for (int i = 0; i < count; i++) {
223
+ int columnType = sqlite3_column_type(currentStatement, i);
224
+ std::string columnName = sqlite3_column_name(currentStatement, i);
225
+
226
+ switch (columnType) {
227
+ case SQLITE_INTEGER:
228
+ case SQLITE_FLOAT:
229
+ row[columnName] = sqlite3_column_double(currentStatement, i);
230
+ break;
231
+ case SQLITE_TEXT: {
232
+ auto columnValue = reinterpret_cast<const char*>(sqlite3_column_text(currentStatement, i));
233
+ row[columnName] = columnValue;
234
+ break;
235
+ }
236
+ case SQLITE_BLOB: {
237
+ int blobSize = sqlite3_column_bytes(currentStatement, i);
238
+ const void* blob = sqlite3_column_blob(currentStatement, i);
239
+ if (blobSize > 0) {
240
+ const auto* blobData = reinterpret_cast<const uint8_t*>(blob);
241
+ row[columnName] = ArrayBuffer::copy(blobData, static_cast<size_t>(blobSize));
242
+ } else {
243
+ row[columnName] = ArrayBuffer::allocate(0);
244
+ }
245
+ break;
246
+ }
247
+ case SQLITE_NULL:
248
+ default:
249
+ row[columnName] = NullType::null;
250
+ break;
251
+ }
252
+ }
253
+
254
+ results.push_back(std::move(row));
255
+ });
256
+
257
+ std::optional<SQLiteQueryTableMetadata> metadata = std::nullopt;
258
+ int count = sqlite3_column_count(statement);
259
+ for (int i = 0; i < count; i++) {
260
+ std::string columnName = sqlite3_column_name(statement, i);
261
+ ColumnType columnDeclaredType = mapSQLiteTypeToColumnType(sqlite3_column_decltype(statement, i));
262
+ auto columnMeta = NitroSQLiteQueryColumnMetadata(columnName, std::move(columnDeclaredType), i);
263
+
264
+ if (!metadata) {
265
+ metadata = std::make_optional<SQLiteQueryTableMetadata>();
266
+ }
267
+ metadata->insert({columnName, std::move(columnMeta)});
268
+ }
269
+
270
+ int rowsAffected = sqlite3_changes(db);
271
+ long long latestInsertRowId = sqlite3_last_insert_rowid(db);
272
+ return std::make_shared<HybridNitroSQLiteQueryResult>(std::move(results), static_cast<double>(latestInsertRowId), rowsAffected,
273
+ std::move(metadata));
274
+ }
275
+
276
+ } // namespace
277
+
278
+ SQLiteConnectionPtr sqliteGetOpenDatabase(const std::string& dbName) {
279
+ return databaseConnections().get(dbName);
280
+ }
281
+
282
+ std::shared_ptr<HybridNitroSQLiteQueryResult> sqliteExecute(const std::string& dbName, const std::string& query,
283
+ const std::optional<SQLiteQueryParams>& params) {
284
+ return sqliteExecute(sqliteGetOpenDatabase(dbName), query, params);
285
+ }
286
+
287
+ std::shared_ptr<HybridNitroSQLiteQueryResult> sqliteExecute(const SQLiteConnectionPtr& connection, const std::string& query,
288
+ const std::optional<SQLiteQueryParams>& params) {
289
+ std::lock_guard lock(connection->mutex);
290
+ sqlite3* db = connection->database;
291
+ if (db == nullptr) {
292
+ throw NitroSQLiteException::DatabaseNotOpen(connection->name);
293
+ }
294
+
295
+ auto statement = prepareStatement(db, query, params);
296
+ return executeStatement(db, statement.get());
297
+ }
298
+
299
+ SQLiteOperationResult sqliteExecuteCommand(const std::string& dbName, const std::string& query,
300
+ const std::optional<SQLiteQueryParams>& params) {
301
+ return sqliteExecuteCommand(sqliteGetOpenDatabase(dbName), query, params);
302
+ }
303
+
304
+ SQLiteOperationResult sqliteExecuteCommand(const SQLiteConnectionPtr& connection, const std::string& query,
305
+ const std::optional<SQLiteQueryParams>& params) {
306
+ std::lock_guard lock(connection->mutex);
307
+ sqlite3* db = connection->database;
308
+ if (db == nullptr) {
309
+ throw NitroSQLiteException::DatabaseNotOpen(connection->name);
310
+ }
311
+
312
+ auto statement = prepareStatement(db, query, params);
313
+ bool isReadOnly = sqlite3_stmt_readonly(statement.get()) != 0;
314
+
315
+ consumeStatement(db, statement.get(), [](sqlite3_stmt*) {});
316
+
317
+ return {.rowsAffected = isReadOnly ? 0 : sqlite3_changes(db)};
318
+ }
319
+
320
+ struct SQLitePreparedStatement::State {
321
+ State(SQLiteConnectionPtr connection, SQLiteStatement statement) : connection(std::move(connection)), statement(std::move(statement)) {}
322
+
323
+ SQLiteConnectionPtr connection;
324
+ SQLiteStatement statement;
325
+ mutable std::mutex mutex;
326
+ };
327
+
328
+ SQLitePreparedStatement::SQLitePreparedStatement(std::shared_ptr<State> state) : _state(std::move(state)) {}
329
+
330
+ SQLitePreparedStatement::~SQLitePreparedStatement() {
331
+ finalize();
332
+ }
333
+
334
+ std::shared_ptr<HybridNitroSQLiteQueryResult> SQLitePreparedStatement::execute(const std::optional<SQLiteQueryParams>& params) {
335
+ std::lock_guard lock(_state->mutex);
336
+ std::lock_guard connectionLock(_state->connection->mutex);
337
+
338
+ if (!_state->statement) {
339
+ throw NitroSQLiteException("Prepared statement has been finalized");
340
+ }
341
+
342
+ sqlite3* database = _state->connection->database;
343
+ if (database == nullptr) {
344
+ throw NitroSQLiteException("Prepared statement belongs to a closed database connection");
345
+ }
346
+
347
+ int resetStatus = sqlite3_reset(_state->statement.get());
348
+ if (resetStatus != SQLITE_OK) {
349
+ throw NitroSQLiteException::SqlExecution(sqlite3_errmsg(database));
350
+ }
351
+
352
+ int clearBindingsStatus = sqlite3_clear_bindings(_state->statement.get());
353
+ if (clearBindingsStatus != SQLITE_OK) {
354
+ throw NitroSQLiteException::SqlExecution(sqlite3_errmsg(database));
355
+ }
356
+
357
+ if (params) {
358
+ bindStatement(_state->statement.get(), *params);
359
+ }
360
+
361
+ try {
362
+ return executeStatement(database, _state->statement.get());
363
+ } catch (...) {
364
+ sqlite3_reset(_state->statement.get());
365
+ throw;
366
+ }
367
+ }
368
+
369
+ void SQLitePreparedStatement::finalize() {
370
+ std::lock_guard lock(_state->mutex);
371
+ std::lock_guard connectionLock(_state->connection->mutex);
372
+ _state->statement.reset();
373
+ }
374
+
375
+ bool SQLitePreparedStatement::isFinalized() const {
376
+ std::lock_guard lock(_state->mutex);
377
+ return !_state->statement;
378
+ }
379
+
380
+ size_t SQLitePreparedStatement::getExternalMemorySize() const noexcept {
381
+ return sizeof(*this) + sizeof(State);
382
+ }
383
+
384
+ std::shared_ptr<SQLitePreparedStatement> sqlitePrepare(const std::string& dbName, const std::string& query) {
385
+ auto connection = sqliteGetOpenDatabase(dbName);
386
+ std::lock_guard lock(connection->mutex);
387
+ if (connection->database == nullptr) {
388
+ throw NitroSQLiteException::DatabaseNotOpen(dbName);
389
+ }
390
+
391
+ auto statement = prepareStatement(connection->database, query, std::nullopt);
392
+ auto state = std::make_shared<SQLitePreparedStatement::State>(connection, std::move(statement));
393
+ return std::shared_ptr<SQLitePreparedStatement>(new SQLitePreparedStatement(std::move(state)));
394
+ }
395
+
396
+ } // namespace margelo::nitro::rnnitrosqlite
@@ -0,0 +1,72 @@
1
+ #pragma once
2
+
3
+ #include "NitroSQLiteDatabaseConnections.hpp"
4
+ #include "hybridObjects/HybridNitroSQLiteQueryResult.hpp"
5
+ #include <functional>
6
+ #include <memory>
7
+ #include <mutex>
8
+ #include <queue>
9
+ #include <sqlite3.h>
10
+ #include <string>
11
+
12
+ namespace margelo::nitro::rnnitrosqlite {
13
+
14
+ /** Open the default connection by database name. Read-only mode requires an existing file. */
15
+ void sqliteOpenDb(const std::string& dbName, const std::string& docPath, bool readOnly = false);
16
+
17
+ /** Open a separate native handle and return its opaque connection ID. */
18
+ std::string sqliteOpenConnection(const std::string& dbName, const std::string& docPath, bool readOnly = false);
19
+
20
+ /** Prepared SQL statement bound to one native connection. */
21
+ class SQLitePreparedStatement {
22
+ public:
23
+ ~SQLitePreparedStatement();
24
+
25
+ /** Reset and execute with new bindings. Throws after finalization or connection closure. */
26
+ std::shared_ptr<HybridNitroSQLiteQueryResult> execute(const std::optional<SQLiteQueryParams>& params);
27
+ /** Release the native statement. Repeated calls are safe. */
28
+ void finalize();
29
+ /** Check whether the native statement has been released. */
30
+ bool isFinalized() const;
31
+ /** Report this object's native memory size to the Nitro runtime. */
32
+ size_t getExternalMemorySize() const noexcept;
33
+
34
+ private:
35
+ struct State;
36
+
37
+ explicit SQLitePreparedStatement(std::shared_ptr<State> state);
38
+
39
+ std::shared_ptr<State> _state;
40
+
41
+ friend std::shared_ptr<SQLitePreparedStatement> sqlitePrepare(const std::string& dbName, const std::string& query);
42
+ };
43
+
44
+ void sqliteCloseDb(const std::string& dbName);
45
+
46
+ /** Delete a database, optionally closing its independent connection first. */
47
+ void sqliteRemoveDb(const std::string& dbName, const std::string& docPath, const std::optional<std::string>& connectionId = std::nullopt,
48
+ const std::optional<std::string>& otherDocPath = std::nullopt);
49
+
50
+ void sqliteAttachDb(const std::string& mainDBName, const std::string& docPath, const std::string& databaseToAttach,
51
+ const std::string& alias);
52
+
53
+ void sqliteDetachDb(const std::string& mainDBName, const std::string& alias);
54
+
55
+ SQLiteConnectionPtr sqliteGetOpenDatabase(const std::string& dbName);
56
+
57
+ std::shared_ptr<HybridNitroSQLiteQueryResult> sqliteExecute(const std::string& dbName, const std::string& query,
58
+ const std::optional<SQLiteQueryParams>& params);
59
+ std::shared_ptr<HybridNitroSQLiteQueryResult> sqliteExecute(const SQLiteConnectionPtr& connection, const std::string& query,
60
+ const std::optional<SQLiteQueryParams>& params);
61
+
62
+ SQLiteOperationResult sqliteExecuteCommand(const std::string& dbName, const std::string& query,
63
+ const std::optional<SQLiteQueryParams>& params = std::nullopt);
64
+ SQLiteOperationResult sqliteExecuteCommand(const SQLiteConnectionPtr& connection, const std::string& query,
65
+ const std::optional<SQLiteQueryParams>& params = std::nullopt);
66
+
67
+ /** Prepare one SQL statement on an open default or independent connection. */
68
+ std::shared_ptr<SQLitePreparedStatement> sqlitePrepare(const std::string& dbName, const std::string& query);
69
+
70
+ void sqliteCloseAll();
71
+
72
+ } // namespace margelo::nitro::rnnitrosqlite
@@ -5,12 +5,9 @@
5
5
  #include <NitroModules/ArrayBuffer.hpp>
6
6
  #include <string>
7
7
 
8
- using namespace margelo::nitro;
9
- using namespace margelo::nitro::rnnitrosqlite;
8
+ namespace margelo::nitro::rnnitrosqlite {
10
9
 
11
- namespace margelo::rnnitrosqlite {
12
-
13
- using SQLiteValue = std::variant<nitro::NullType, bool, std::shared_ptr<ArrayBuffer>, std::string, double>;
10
+ using SQLiteValue = std::variant<NullType, bool, std::shared_ptr<ArrayBuffer>, std::string, double>;
14
11
  using SQLiteQueryParams = std::vector<SQLiteValue>;
15
12
  using SQLiteQueryResultRow = std::unordered_map<std::string, SQLiteValue>;
16
13
  using SQLiteQueryResults = std::vector<SQLiteQueryResultRow>;
@@ -40,4 +37,4 @@ inline ColumnType mapSQLiteTypeToColumnType(const char* type) {
40
37
  }
41
38
  }
42
39
 
43
- } // namespace margelo::rnnitrosqlite
40
+ } // namespace margelo::nitro::rnnitrosqlite
@@ -3,7 +3,7 @@
3
3
  #include <string>
4
4
  #include <sys/stat.h>
5
5
 
6
- namespace margelo::rnnitrosqlite {
6
+ namespace margelo::nitro::rnnitrosqlite {
7
7
 
8
8
  bool folder_exists(const std::string& foldername) {
9
9
  struct stat buffer;
@@ -57,4 +57,4 @@ std::string get_db_path(const std::string& dbName, const std::string& docPath) {
57
57
  return docPath + "/" + dbName;
58
58
  }
59
59
 
60
- } // namespace margelo::rnnitrosqlite
60
+ } // namespace margelo::nitro::rnnitrosqlite