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
package/src/types.ts CHANGED
@@ -1,104 +1,218 @@
1
1
  import type { NitroSQLiteQueryResult } from './specs/NitroSQLiteQueryResult.nitro'
2
2
 
3
+ /** Options for `open()` and `NitroSQLite.open()`. */
3
4
  export interface NitroSQLiteConnectionOptions {
5
+ /** Database file name. Only one managed connection may use a name at a time. */
4
6
  name: string
7
+ /** Directory relative to the platform's database directory. */
5
8
  location?: string
9
+ /**
10
+ * Choose a connection to the database file. The default connection is addressed
11
+ * by `name`, so opening it twice throws. Each `independent` connection has its
12
+ * own native handle and operation queue and can share a file with other connections.
13
+ * Defaults to `'default'`.
14
+ */
15
+ connection?: 'default' | 'independent'
16
+ /**
17
+ * Open an existing database without write access. Opening fails if the file does
18
+ * not exist. A read-only connection cannot write, attach, or delete a database.
19
+ * Defaults to `false`.
20
+ */
21
+ readOnly?: boolean
6
22
  }
7
23
 
24
+ /** A managed connection bound to one database name. Do not use it after closing or deleting it. */
8
25
  export interface NitroSQLiteConnection {
26
+ /** Close the connection. Throws if a queued operation is running or pending. */
9
27
  close(): void
28
+ /** Delete the database file and end this connection. Throws if the connection is busy.
29
+ * This also closes the native handle if it is open.
30
+ */
10
31
  delete(): void
32
+ /** Attach another database under `alias` for queries against this connection.
33
+ * @param dbNameToAttach File name of the database to attach.
34
+ * @param alias SQL schema name for the attached database.
35
+ * @param location Directory relative to the platform database directory.
36
+ */
11
37
  attach(dbNameToAttach: string, alias: string, location?: string): void
38
+ /** Detach the database identified by `alias`.
39
+ * @param alias SQL schema name used when attaching the database.
40
+ */
12
41
  detach(alias: string): void
42
+ /** Run a callback in a queued transaction. The callback must use `tx` for database work.
43
+ * It commits on success and rolls back on error unless explicitly finalized.
44
+ * Awaiting another queued operation for this database inside the callback deadlocks.
45
+ * Synchronous connection methods throw while this transaction is active.
46
+ * @param transactionCallback Async callback receiving the transaction handle.
47
+ * @returns The callback's result after the transaction finishes.
48
+ */
13
49
  transaction: <Result = void>(
14
50
  transactionCallback: (tx: Transaction) => Promise<Result>,
15
51
  ) => Promise<Result>
52
+ /** Execute one SQL statement synchronously. Throws while the connection is busy. */
16
53
  execute: ExecuteQuery
54
+ /** Queue one SQL statement and resolve with its result. */
17
55
  executeAsync: ExecuteAsyncQuery
56
+ /**
57
+ * Prepare one SQL statement on this connection for repeated execution.
58
+ * Finalize the returned statement before closing the connection.
59
+ * @param query SQL statement with optional positional placeholders.
60
+ */
61
+ prepare(query: string): PreparedStatement
62
+ /** Execute commands in one exclusive transaction. Throws while the connection is busy.
63
+ * @param commands SQL commands and optional parameter sets.
64
+ * @returns Total affected row count.
65
+ */
18
66
  executeBatch(commands: BatchQueryCommand[]): BatchQueryResult
67
+ /** Queue commands in one exclusive transaction.
68
+ * @param commands SQL commands and optional parameter sets.
69
+ * @returns A promise of the total affected row count.
70
+ */
19
71
  executeBatchAsync(commands: BatchQueryCommand[]): Promise<BatchQueryResult>
72
+ /** Execute one non-empty SQL command per file line in an exclusive transaction.
73
+ * `location` is a path to the SQL file; multi-line statements are unsupported.
74
+ * @param location Path to the SQL file.
75
+ * @returns Number of executed commands and affected rows.
76
+ */
20
77
  loadFile(location: string): FileLoadResult
78
+ /** Queue the file import and resolve with its command and row counts.
79
+ * @param location Path to the SQL file.
80
+ */
21
81
  loadFileAsync(location: string): Promise<FileLoadResult>
22
82
  }
23
83
 
84
+ /** SQLite value categories reported in query column metadata. */
24
85
  export enum ColumnType {
86
+ /** Boolean declaration. */
25
87
  BOOLEAN,
88
+ /** Floating point declaration. */
26
89
  NUMBER,
90
+ /** Integer declaration. */
27
91
  INT64,
92
+ /** Text declaration. */
28
93
  TEXT,
94
+ /** Blob declaration. */
29
95
  ARRAY_BUFFER,
96
+ /** Missing or unrecognized declaration. */
30
97
  NULL_VALUE,
31
98
  }
32
99
 
100
+ /** Values accepted by bound SQL parameters and returned in result rows. */
33
101
  export type SQLiteValue = boolean | number | string | ArrayBuffer | null
34
102
 
103
+ /** Positional values for SQL placeholders. */
35
104
  export type SQLiteQueryParams = SQLiteValue[]
36
105
 
106
+ /** A row keyed by result column names. */
37
107
  export type QueryResultRow = Record<string, SQLiteValue>
38
108
 
109
+ /** Query result with a row adapter for TypeORM-style consumers. */
39
110
  export type QueryResult<Row extends QueryResultRow = QueryResultRow> =
40
111
  NitroSQLiteQueryResult & {
41
- /** Query results in a row format for TypeORM compatibility */
112
+ /** Query rows in a TypeORM-compatible collection. */
42
113
  rows: NitroSQLiteQueryResultRows<Row>
43
114
  }
44
115
 
116
+ /** Indexable view of query rows. */
45
117
  export type NitroSQLiteQueryResultRows<
46
118
  Row extends Record<string, SQLiteValue> = Record<string, SQLiteValue>,
47
119
  > = {
48
- /** Raw array with all dataset */
120
+ /** All returned rows. */
49
121
  _array: Row[]
50
122
 
51
- /** The lengh of the dataset */
123
+ /** Number of returned rows. */
52
124
  length: number
53
125
 
54
- /** A convenience function to acess the index based the row object
55
- * @param idx the row index
56
- * @returns the row structure identified by column names
126
+ /** Get a row by zero-based index; returns `undefined` when out of range.
127
+ * @param idx Row index.
57
128
  */
58
129
  item: (idx: number) => Row | undefined
59
130
  }
60
131
 
132
+ /** Execute one SQL statement synchronously and return its typed rows.
133
+ * @param query SQL statement with optional positional placeholders.
134
+ * @param params Values bound to the placeholders.
135
+ */
61
136
  export type ExecuteQuery = <Row extends QueryResultRow = QueryResultRow>(
62
137
  query: string,
63
138
  params?: SQLiteValue[],
64
139
  ) => QueryResult<Row>
65
140
 
141
+ /** Queue one SQL statement and resolve with its typed rows.
142
+ * @param query SQL statement with optional positional placeholders.
143
+ * @param params Values bound to the placeholders.
144
+ */
66
145
  export type ExecuteAsyncQuery = <Row extends QueryResultRow = QueryResultRow>(
67
146
  query: string,
68
147
  params?: SQLiteQueryParams,
69
148
  ) => Promise<QueryResult<Row>>
70
149
 
150
+ /**
151
+ * A reusable SQL statement bound to the connection that prepared it. Each
152
+ * execution resets the statement and its bindings before applying new values.
153
+ * Finalize it before closing the connection.
154
+ */
155
+ export interface PreparedStatement {
156
+ /** Whether `finalize()` has released the native statement. */
157
+ readonly isFinalized: boolean
158
+ /** Execute on the calling thread. Throws if the statement is finalized, its connection is closed, or the managed connection is busy. */
159
+ execute: ExecutePreparedStatement
160
+ /** Queue execution on a background thread and resolve with the query result. */
161
+ executeAsync: ExecutePreparedStatementAsync
162
+ /** Release the native statement. Calling this more than once is safe. */
163
+ finalize(): void
164
+ }
165
+
166
+ /**
167
+ * Execute a prepared statement with optional positional values and return typed rows.
168
+ * Each call replaces the previous parameter bindings.
169
+ * @param params Values bound to the statement's positional placeholders.
170
+ */
171
+ export type ExecutePreparedStatement = <
172
+ Row extends QueryResultRow = QueryResultRow,
173
+ >(
174
+ params?: SQLiteQueryParams,
175
+ ) => QueryResult<Row>
176
+
177
+ /**
178
+ * Queue a prepared statement execution and resolve with typed rows. Calls on the
179
+ * same managed connection run in queue order.
180
+ * @param params Values bound to the statement's positional placeholders.
181
+ */
182
+ export type ExecutePreparedStatementAsync = <
183
+ Row extends QueryResultRow = QueryResultRow,
184
+ >(
185
+ params?: SQLiteQueryParams,
186
+ ) => Promise<QueryResult<Row>>
187
+
188
+ /** Handle valid only while its transaction callback is active. */
71
189
  export interface Transaction {
190
+ /** Commit now. Further operations on this transaction throw. */
72
191
  commit(): NitroSQLiteQueryResult
192
+ /** Roll back now. Further operations on this transaction throw. */
73
193
  rollback(): NitroSQLiteQueryResult
194
+ /** Execute within this transaction on the calling thread. */
74
195
  execute: ExecuteQuery
196
+ /** Execute within this transaction on a background thread. */
75
197
  executeAsync: ExecuteAsyncQuery
76
198
  }
77
199
 
78
- /**
79
- * Allows the execution of bulk of sql commands
80
- * inside a transaction
81
- * If a single query must be executed many times with different arguments, its preferred
82
- * to declare it a single time, and use an array of array parameters.
83
- */
200
+ /** One command in a batch. A nested `params` array repeats the query for each parameter set. */
84
201
  export interface BatchQueryCommand {
202
+ /** SQL statement to execute. */
85
203
  query: string
204
+ /** One parameter set, or several sets for repeated execution. */
86
205
  params?: SQLiteQueryParams | SQLiteQueryParams[]
87
206
  }
88
207
 
89
- /**
90
- * status: 0 or undefined for correct execution, 1 for error
91
- * message: if status === 1, here you will find error description
92
- * rowsAffected: Number of affected rows if status == 0
93
- */
208
+ /** Result of a committed SQL batch. Errors are thrown rather than returned here. */
94
209
  export interface BatchQueryResult {
210
+ /** Total number of rows changed by the batch. */
95
211
  rowsAffected?: number
96
212
  }
97
213
 
98
- /**
99
- * Result of loading a file and executing every line as a SQL command
100
- * Similar to BatchQueryResult
101
- */
214
+ /** Result of importing a SQL file in one transaction. */
102
215
  export interface FileLoadResult extends BatchQueryResult {
216
+ /** Number of non-empty lines executed as SQL commands. */
103
217
  commands?: number
104
218
  }
@@ -1,358 +0,0 @@
1
- #include "operations.hpp"
2
- #include "NitroSQLiteException.hpp"
3
- #include "hybridObjects/HybridNitroSQLiteQueryResult.hpp"
4
- #include "logs.hpp"
5
- #include "utils.hpp"
6
- #include <NitroModules/ArrayBuffer.hpp>
7
- #include <cmath>
8
- #include <ctime>
9
- #include <iostream>
10
- #include <limits>
11
- #include <map>
12
- #include <memory>
13
- #include <mutex>
14
- #include <optional>
15
- #include <sqlite3.h>
16
- #include <sstream>
17
- #include <unistd.h>
18
-
19
- #ifdef NITRO_SQLITE_VEC
20
- // Angle-bracket so it resolves via -I (CocoaPods intercepts quoted includes).
21
- #include <registerVectorExtensions.hpp>
22
- #endif
23
-
24
- using namespace facebook;
25
- using namespace margelo::nitro;
26
- using namespace margelo::nitro::rnnitrosqlite;
27
-
28
- namespace margelo::rnnitrosqlite {
29
-
30
- static constexpr double kInt64MinAsDouble = static_cast<double>(std::numeric_limits<int64_t>::min());
31
- static constexpr double kInt64UpperBoundAsDouble = -kInt64MinAsDouble;
32
-
33
- namespace {
34
-
35
- std::map<std::string, SQLiteConnectionPtr> dbMap;
36
- std::mutex dbMapMutex;
37
- std::mutex dbLifecycleMutex;
38
-
39
- } // namespace
40
-
41
- SQLiteConnection::SQLiteConnection(std::string connectionName, sqlite3* database) : name(std::move(connectionName)), database(database) {}
42
-
43
- SQLiteConnection::~SQLiteConnection() {
44
- close();
45
- }
46
-
47
- void SQLiteConnection::close() noexcept {
48
- std::lock_guard lock(mutex);
49
- if (database == nullptr) {
50
- return;
51
- }
52
-
53
- sqlite3_close_v2(database);
54
- database = nullptr;
55
- }
56
-
57
- void sqliteOpenDb(const std::string& dbName, const std::string& docPath) {
58
- std::lock_guard lifecycleLock(dbLifecycleMutex);
59
- {
60
- std::lock_guard lock(dbMapMutex);
61
- if (dbMap.contains(dbName)) {
62
- throw NitroSQLiteException::DatabaseAlreadyOpen(dbName);
63
- }
64
- }
65
-
66
- #ifdef NITRO_SQLITE_VEC
67
- // Register before opening so the connection exposes vec0 + vec_*.
68
- margelo::rnnitrosqlitevec::registerVectorExtensions();
69
- #endif
70
-
71
- std::string dbPath = get_db_path(dbName, docPath);
72
-
73
- int sqlOpenFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX;
74
-
75
- sqlite3* rawDatabase = nullptr;
76
- const int openStatus = sqlite3_open_v2(dbPath.c_str(), &rawDatabase, sqlOpenFlags, nullptr);
77
- std::unique_ptr<sqlite3, decltype(&sqlite3_close_v2)> database(rawDatabase, sqlite3_close_v2);
78
-
79
- if (openStatus != SQLITE_OK) {
80
- const std::string errorMessage = rawDatabase == nullptr ? sqlite3_errstr(openStatus) : sqlite3_errmsg(rawDatabase);
81
- throw NitroSQLiteException(NitroSQLiteExceptionType::DatabaseCannotBeOpened, errorMessage);
82
- }
83
-
84
- auto connection = std::make_shared<SQLiteConnection>(dbName, database.get());
85
- database.release();
86
- {
87
- std::lock_guard lock(dbMapMutex);
88
- const bool inserted = dbMap.emplace(dbName, connection).second;
89
- if (!inserted) {
90
- throw NitroSQLiteException::DatabaseAlreadyOpen(dbName);
91
- }
92
- }
93
- }
94
-
95
- void sqliteCloseDb(const std::string& dbName) {
96
- std::lock_guard lifecycleLock(dbLifecycleMutex);
97
- SQLiteConnectionPtr connection;
98
- {
99
- std::lock_guard lock(dbMapMutex);
100
- auto iterator = dbMap.find(dbName);
101
- if (iterator == dbMap.end()) {
102
- throw NitroSQLiteException::DatabaseNotOpen(dbName);
103
- }
104
-
105
- connection = std::move(iterator->second);
106
- dbMap.erase(iterator);
107
- }
108
-
109
- connection->close();
110
- }
111
-
112
- void sqliteCloseAll() {
113
- std::lock_guard lifecycleLock(dbLifecycleMutex);
114
- std::map<std::string, SQLiteConnectionPtr> connections;
115
- {
116
- std::lock_guard lock(dbMapMutex);
117
- connections.swap(dbMap);
118
- }
119
-
120
- for (const auto& [_, connection] : connections) {
121
- connection->close();
122
- }
123
- }
124
-
125
- void sqliteAttachDb(const std::string& mainDBName, const std::string& docPath, const std::string& databaseToAttach,
126
- const std::string& alias) {
127
- /**
128
- * There is no need to check if mainDBName is opened because sqliteExecuteCommand will do that.
129
- * */
130
- std::string dbPath = get_db_path(databaseToAttach, docPath);
131
- std::string statement = "ATTACH DATABASE '" + dbPath + "' AS " + alias;
132
-
133
- try {
134
- sqliteExecuteCommand(mainDBName, statement);
135
- } catch (NitroSQLiteException& e) {
136
- throw NitroSQLiteException(NitroSQLiteExceptionType::UnableToAttachToDatabase,
137
- mainDBName + " was unable to attach another database: " + std::string(e.what()));
138
- }
139
- }
140
-
141
- void sqliteDetachDb(const std::string& mainDBName, const std::string& alias) {
142
- /**
143
- * There is no need to check if mainDBName is opened because sqliteExecuteCommand will do that.
144
- * */
145
- std::string statement = "DETACH DATABASE " + alias;
146
-
147
- try {
148
- sqliteExecuteCommand(mainDBName, statement);
149
- } catch (NitroSQLiteException& e) {
150
- throw NitroSQLiteException(NitroSQLiteExceptionType::UnableToAttachToDatabase,
151
- mainDBName + " was unable to detach database: " + std::string(e.what()));
152
- }
153
- }
154
-
155
- void sqliteRemoveDb(const std::string& dbName, const std::string& docPath) {
156
- std::lock_guard lifecycleLock(dbLifecycleMutex);
157
- const std::string dbFilePath = get_db_path(dbName, docPath);
158
- if (!file_exists(dbFilePath)) {
159
- throw NitroSQLiteException::DatabaseFileNotFound(dbFilePath);
160
- }
161
-
162
- SQLiteConnectionPtr connection;
163
- {
164
- std::lock_guard lock(dbMapMutex);
165
- auto iterator = dbMap.find(dbName);
166
- if (iterator != dbMap.end()) {
167
- connection = std::move(iterator->second);
168
- dbMap.erase(iterator);
169
- }
170
- }
171
-
172
- if (connection) {
173
- connection->close();
174
- }
175
-
176
- remove(dbFilePath.c_str());
177
- }
178
-
179
- void bindStatement(sqlite3_stmt* statement, const SQLiteQueryParams& values) {
180
- for (int valueIndex = 0; valueIndex < values.size(); valueIndex++) {
181
- int sqliteIndex = valueIndex + 1;
182
- SQLiteValue value = values.at(valueIndex);
183
- if (std::holds_alternative<NullType>(value)) {
184
- sqlite3_bind_null(statement, sqliteIndex);
185
- } else if (std::holds_alternative<bool>(value)) {
186
- sqlite3_bind_int(statement, sqliteIndex, std::get<bool>(value));
187
- } else if (std::holds_alternative<double>(value)) {
188
- // Bind whole numbers as INTEGER so vec0 rowid/pk/partition (which reject REAL) work; SQLite still coerces to REAL for REAL columns.
189
- double doubleValue = std::get<double>(value);
190
- if (std::trunc(doubleValue) == doubleValue && doubleValue >= kInt64MinAsDouble && doubleValue < kInt64UpperBoundAsDouble) {
191
- sqlite3_bind_int64(statement, sqliteIndex, static_cast<sqlite3_int64>(doubleValue));
192
- } else {
193
- sqlite3_bind_double(statement, sqliteIndex, doubleValue);
194
- }
195
- } else if (std::holds_alternative<std::string>(value)) {
196
- const auto stringValue = std::get<std::string>(value);
197
- sqlite3_bind_text(statement, sqliteIndex, stringValue.c_str(), stringValue.length(), SQLITE_TRANSIENT);
198
- } else if (std::holds_alternative<std::shared_ptr<ArrayBuffer>>(value)) {
199
- const auto arrayBufferValue = std::get<std::shared_ptr<ArrayBuffer>>(value);
200
- sqlite3_bind_blob(statement, sqliteIndex, arrayBufferValue->data(), arrayBufferValue->size(), SQLITE_STATIC);
201
- }
202
- }
203
- }
204
-
205
- namespace {
206
-
207
- struct SQLiteStatementFinalizer {
208
- void operator()(sqlite3_stmt* statement) const noexcept {
209
- if (statement != nullptr) {
210
- sqlite3_finalize(statement);
211
- }
212
- }
213
- };
214
-
215
- using SQLiteStatement = std::unique_ptr<sqlite3_stmt, SQLiteStatementFinalizer>;
216
-
217
- SQLiteStatement prepareStatement(sqlite3* db, const std::string& query, const std::optional<SQLiteQueryParams>& params) {
218
- sqlite3_stmt* rawStatement = nullptr;
219
- int statementStatus = sqlite3_prepare_v2(db, query.c_str(), -1, &rawStatement, nullptr);
220
- SQLiteStatement statement(rawStatement);
221
-
222
- if (statementStatus != SQLITE_OK) {
223
- throw NitroSQLiteException::SqlExecution(sqlite3_errmsg(db));
224
- }
225
-
226
- if (params) {
227
- bindStatement(statement.get(), *params);
228
- }
229
-
230
- return statement;
231
- }
232
-
233
- template <typename OnRow>
234
- void consumeStatement(sqlite3* db, sqlite3_stmt* statement, OnRow&& onRow) {
235
- while (true) {
236
- int result = sqlite3_step(statement);
237
-
238
- if (result == SQLITE_ROW) {
239
- onRow(statement);
240
- continue;
241
- }
242
-
243
- if (result == SQLITE_DONE) {
244
- return;
245
- }
246
-
247
- throw NitroSQLiteException::SqlExecution(sqlite3_errmsg(db));
248
- }
249
- }
250
-
251
- } // namespace
252
-
253
- SQLiteConnectionPtr sqliteGetOpenDatabase(const std::string& dbName) {
254
- std::lock_guard lock(dbMapMutex);
255
- auto iterator = dbMap.find(dbName);
256
- if (iterator == dbMap.end()) {
257
- throw NitroSQLiteException::DatabaseNotOpen(dbName);
258
- }
259
-
260
- return iterator->second;
261
- }
262
-
263
- std::shared_ptr<HybridNitroSQLiteQueryResult> sqliteExecute(const std::string& dbName, const std::string& query,
264
- const std::optional<SQLiteQueryParams>& params) {
265
- return sqliteExecute(sqliteGetOpenDatabase(dbName), query, params);
266
- }
267
-
268
- std::shared_ptr<HybridNitroSQLiteQueryResult> sqliteExecute(const SQLiteConnectionPtr& connection, const std::string& query,
269
- const std::optional<SQLiteQueryParams>& params) {
270
- std::lock_guard lock(connection->mutex);
271
- sqlite3* db = connection->database;
272
- if (db == nullptr) {
273
- throw NitroSQLiteException::DatabaseNotOpen(connection->name);
274
- }
275
-
276
- auto statement = prepareStatement(db, query, params);
277
- SQLiteQueryResults results;
278
-
279
- consumeStatement(db, statement.get(), [&](sqlite3_stmt* currentStatement) {
280
- SQLiteQueryResultRow row;
281
- int count = sqlite3_column_count(currentStatement);
282
-
283
- for (int i = 0; i < count; i++) {
284
- int columnType = sqlite3_column_type(currentStatement, i);
285
- std::string columnName = sqlite3_column_name(currentStatement, i);
286
-
287
- switch (columnType) {
288
- case SQLITE_INTEGER:
289
- case SQLITE_FLOAT:
290
- row[columnName] = sqlite3_column_double(currentStatement, i);
291
- break;
292
- case SQLITE_TEXT: {
293
- auto columnValue = reinterpret_cast<const char*>(sqlite3_column_text(currentStatement, i));
294
- row[columnName] = columnValue;
295
- break;
296
- }
297
- case SQLITE_BLOB: {
298
- int blobSize = sqlite3_column_bytes(currentStatement, i);
299
- const void* blob = sqlite3_column_blob(currentStatement, i);
300
- if (blobSize > 0) {
301
- const auto* blobData = reinterpret_cast<const uint8_t*>(blob);
302
- row[columnName] = ArrayBuffer::copy(blobData, static_cast<size_t>(blobSize));
303
- } else {
304
- row[columnName] = ArrayBuffer::allocate(0);
305
- }
306
- break;
307
- }
308
- case SQLITE_NULL:
309
- default:
310
- row[columnName] = NullType::null;
311
- break;
312
- }
313
- }
314
-
315
- results.push_back(std::move(row));
316
- });
317
-
318
- std::optional<SQLiteQueryTableMetadata> metadata = std::nullopt;
319
- int count = sqlite3_column_count(statement.get());
320
- for (int i = 0; i < count; i++) {
321
- std::string columnName = sqlite3_column_name(statement.get(), i);
322
- ColumnType columnDeclaredType = mapSQLiteTypeToColumnType(sqlite3_column_decltype(statement.get(), i));
323
- auto columnMeta = NitroSQLiteQueryColumnMetadata(columnName, std::move(columnDeclaredType), i);
324
-
325
- if (!metadata) {
326
- metadata = std::make_optional<SQLiteQueryTableMetadata>();
327
- }
328
- metadata->insert({columnName, std::move(columnMeta)});
329
- }
330
-
331
- int rowsAffected = sqlite3_changes(db);
332
- long long latestInsertRowId = sqlite3_last_insert_rowid(db);
333
- return std::make_shared<HybridNitroSQLiteQueryResult>(std::move(results), static_cast<double>(latestInsertRowId), rowsAffected,
334
- std::move(metadata));
335
- }
336
-
337
- SQLiteOperationResult sqliteExecuteCommand(const std::string& dbName, const std::string& query,
338
- const std::optional<SQLiteQueryParams>& params) {
339
- return sqliteExecuteCommand(sqliteGetOpenDatabase(dbName), query, params);
340
- }
341
-
342
- SQLiteOperationResult sqliteExecuteCommand(const SQLiteConnectionPtr& connection, const std::string& query,
343
- const std::optional<SQLiteQueryParams>& params) {
344
- std::lock_guard lock(connection->mutex);
345
- sqlite3* db = connection->database;
346
- if (db == nullptr) {
347
- throw NitroSQLiteException::DatabaseNotOpen(connection->name);
348
- }
349
-
350
- auto statement = prepareStatement(db, query, params);
351
- bool isReadOnly = sqlite3_stmt_readonly(statement.get()) != 0;
352
-
353
- consumeStatement(db, statement.get(), [](sqlite3_stmt*) {});
354
-
355
- return {.rowsAffected = isReadOnly ? 0 : sqlite3_changes(db)};
356
- }
357
-
358
- } // namespace margelo::rnnitrosqlite
@@ -1,56 +0,0 @@
1
- #pragma once
2
-
3
- #include "hybridObjects/HybridNitroSQLiteQueryResult.hpp"
4
- #include "types.hpp"
5
- #include <memory>
6
- #include <mutex>
7
- #include <sqlite3.h>
8
- #include <string>
9
-
10
- namespace margelo::rnnitrosqlite {
11
-
12
- // Calls against one connection are serialized by `mutex`. Separate connections
13
- // intentionally remain independent, so SQLITE_THREADSAFE=0 still requires the
14
- // caller to serialize SQLite calls globally.
15
- struct SQLiteConnection final {
16
- SQLiteConnection(std::string name, sqlite3* database);
17
- ~SQLiteConnection();
18
-
19
- SQLiteConnection(const SQLiteConnection&) = delete;
20
- SQLiteConnection& operator=(const SQLiteConnection&) = delete;
21
-
22
- void close() noexcept;
23
-
24
- const std::string name;
25
- sqlite3* database;
26
- std::recursive_mutex mutex;
27
- };
28
-
29
- using SQLiteConnectionPtr = std::shared_ptr<SQLiteConnection>;
30
-
31
- void sqliteOpenDb(const std::string& dbName, const std::string& docPath);
32
-
33
- void sqliteCloseDb(const std::string& dbName);
34
-
35
- void sqliteRemoveDb(const std::string& dbName, const std::string& docPath);
36
-
37
- void sqliteAttachDb(const std::string& mainDBName, const std::string& docPath, const std::string& databaseToAttach,
38
- const std::string& alias);
39
-
40
- void sqliteDetachDb(const std::string& mainDBName, const std::string& alias);
41
-
42
- SQLiteConnectionPtr sqliteGetOpenDatabase(const std::string& dbName);
43
-
44
- std::shared_ptr<HybridNitroSQLiteQueryResult> sqliteExecute(const std::string& dbName, const std::string& query,
45
- const std::optional<SQLiteQueryParams>& params);
46
- std::shared_ptr<HybridNitroSQLiteQueryResult> sqliteExecute(const SQLiteConnectionPtr& connection, const std::string& query,
47
- const std::optional<SQLiteQueryParams>& params);
48
-
49
- SQLiteOperationResult sqliteExecuteCommand(const std::string& dbName, const std::string& query,
50
- const std::optional<SQLiteQueryParams>& params = std::nullopt);
51
- SQLiteOperationResult sqliteExecuteCommand(const SQLiteConnectionPtr& connection, const std::string& query,
52
- const std::optional<SQLiteQueryParams>& params = std::nullopt);
53
-
54
- void sqliteCloseAll();
55
-
56
- } // namespace margelo::rnnitrosqlite
File without changes