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
@@ -51,7 +51,11 @@ Pod::Spec.new do |s|
51
51
  s.homepage = package["homepage"]
52
52
  s.license = package["license"]
53
53
  s.authors = package["author"]
54
- s.platforms = { :ios => min_ios_version_supported, :visionos => "1.0" }
54
+ s.platforms = {
55
+ :ios => min_ios_version_supported,
56
+ :visionos => "1.0",
57
+ :osx => "10.13",
58
+ }
55
59
  s.source = { :git => "https://github.com/margelo/react-native-nitro-sqlite.git", :tag => "#{s.version}" }
56
60
 
57
61
  # Opt-in vector search (NITRO_SQLITE_VEC=1); the companion pod compiles the sources.
@@ -59,9 +63,9 @@ Pod::Spec.new do |s|
59
63
  nitro_sqlite_vec_cpp = File.expand_path(File.join(__dir__, "..", "react-native-nitro-sqlite-vec", "cpp"))
60
64
 
61
65
  s.source_files = [
62
- # Implementation (Swift)
66
+ # Apple platform implementation (Swift)
63
67
  "ios/**/*.{swift}",
64
- # Autolinking/Registration (Objective-C++)
68
+ # Apple platform autolinking/registration (Objective-C++)
65
69
  "ios/**/*.{h,hpp,m,mm}",
66
70
  # Implementation (C++ objects)
67
71
  "cpp/**/*.{h,hpp,c,cpp}"
@@ -5,13 +5,14 @@ set (CMAKE_VERBOSE_MAKEFILE ON)
5
5
  set (CMAKE_CXX_STANDARD 20)
6
6
 
7
7
  add_definitions(
8
+ ${SQLITE_DEFAULT_FLAGS}
8
9
  ${SQLITE_FLAGS}
9
10
  )
10
11
 
11
12
  file(GLOB_RECURSE cpp_files RELATIVE ${CMAKE_SOURCE_DIR}
12
13
  "../cpp/**.c"
13
14
  "../cpp/**.cpp"
14
- "cpp-adapter.cpp"
15
+ "RNNitroSQLiteAdapter.cpp"
15
16
  )
16
17
 
17
18
  # Create library "RNNitroSQLite" and add all C++ files to it
@@ -34,7 +35,7 @@ if(NITRO_SQLITE_VEC)
34
35
  message(STATUS "[react-native-nitro-sqlite] vector search ENABLED (sqlite-vec from ${NITRO_SQLITE_VEC_CPP_DIR})")
35
36
  set(NITRO_SQLITE_VEC_SOURCES
36
37
  "${NITRO_SQLITE_VEC_CPP_DIR}/sqlite-vec/sqlite-vec.c"
37
- "${NITRO_SQLITE_VEC_CPP_DIR}/registerVectorExtensions.cpp"
38
+ "${NITRO_SQLITE_VEC_CPP_DIR}/NitroSQLiteVecRegisterVectorExtensions.cpp"
38
39
  )
39
40
  target_sources(${CMAKE_PROJECT_NAME} PRIVATE ${NITRO_SQLITE_VEC_SOURCES})
40
41
  # SQLITE_CORE: link against our bundled sqlite3; SQLITE_VEC_STATIC: drop Windows dllexport.
@@ -73,6 +73,7 @@ file("$reactNativeRootDir/ReactAndroid/gradle.properties").withInputStream { rea
73
73
  def REACT_NATIVE_VERSION = reactProperties.getProperty("VERSION_NAME")
74
74
  def REACT_NATIVE_MINOR_VERSION = REACT_NATIVE_VERSION.startsWith("0.0.0-") ? 1000 : REACT_NATIVE_VERSION.split("\\.")[1].toInteger()
75
75
 
76
+ apply from: file('sqlite-flags.gradle')
76
77
  def SQLITE_FLAGS = rootProject.properties['nitroSqliteFlags']
77
78
 
78
79
  // Opt-in: resolve the companion's cpp dir so CMake compiles its sqlite-vec into this library.
@@ -122,6 +123,7 @@ android {
122
123
  arguments "-DANDROID_STL=c++_shared",
123
124
  "-DANDROID_TOOLCHAIN=clang",
124
125
  "-DREACT_NATIVE_MINOR_VERSION=${REACT_NATIVE_MINOR_VERSION}",
126
+ "-DSQLITE_DEFAULT_FLAGS=${nitroSqliteDefaultFlags.join(';')}",
125
127
  "-DSQLITE_FLAGS='${SQLITE_FLAGS ? SQLITE_FLAGS : ''}'",
126
128
  "-DNITRO_SQLITE_VEC=${NITRO_SQLITE_VEC_ENABLED ? 'ON' : 'OFF'}",
127
129
  "-DNITRO_SQLITE_VEC_CPP_DIR=${NITRO_SQLITE_VEC_CPP_DIR}",
@@ -0,0 +1,47 @@
1
+ import groovy.json.JsonSlurper
2
+
3
+ project.ext.resolveNitroSqliteDefaultFlags = { File appPackageFile, String customFlags ->
4
+ def appPackage = appPackageFile.exists() ? new JsonSlurper().parse(appPackageFile) : [:]
5
+ def appConfig = appPackage.get("nitroSQLite", [:])
6
+
7
+ if (!(appConfig instanceof Map)) {
8
+ throw new GradleException("nitroSQLite in package.json must be an object")
9
+ }
10
+
11
+ def readBooleanFlag = { String name ->
12
+ def value = appConfig.get(name, true)
13
+ if (!(value instanceof Boolean)) {
14
+ throw new GradleException("nitroSQLite.${name} in package.json must be true or false")
15
+ }
16
+ value
17
+ }
18
+
19
+ def threadSafe = readBooleanFlag("threadSafe")
20
+ def performanceMode = readBooleanFlag("performanceMode")
21
+ def customFlagNames = (customFlags =~ /-D([A-Za-z_][A-Za-z0-9_]*)/).collect { it[1] }.toSet()
22
+
23
+ def defaultFlags = [threadSafe ? '-DSQLITE_THREADSAFE=1' : '-DSQLITE_THREADSAFE=0']
24
+ if (performanceMode) {
25
+ defaultFlags += [
26
+ "-DSQLITE_DQS=0",
27
+ "-DSQLITE_DEFAULT_MEMSTATUS=0",
28
+ "-DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1",
29
+ "-DSQLITE_LIKE_DOESNT_MATCH_BLOBS=1",
30
+ "-DSQLITE_MAX_EXPR_DEPTH=0",
31
+ "-DSQLITE_OMIT_DEPRECATED=1",
32
+ "-DSQLITE_OMIT_PROGRESS_CALLBACK=1",
33
+ "-DSQLITE_OMIT_SHARED_CACHE=1",
34
+ "-DSQLITE_USE_ALLOCA=1",
35
+ ]
36
+ }
37
+
38
+ // Preserve nitroSqliteFlags as an override for individual compile definitions.
39
+ defaultFlags.findAll { flag ->
40
+ def name = flag.substring(2).split("=")[0]
41
+ !customFlagNames.contains(name) && !(name == "SQLITE_THREADSAFE" && customFlagNames.contains("THREADSAFE"))
42
+ }
43
+ }
44
+
45
+ def appPackageFile = new File(rootProject.projectDir.parentFile, "package.json")
46
+ def customFlags = (rootProject.findProperty("nitroSqliteFlags") ?: "").toString()
47
+ project.ext.nitroSqliteDefaultFlags = resolveNitroSqliteDefaultFlags(appPackageFile, customFlags)
@@ -0,0 +1,306 @@
1
+ #include "NitroSQLiteDatabaseConnections.hpp"
2
+ #include "NitroSQLiteDatabaseMigration.hpp"
3
+ #include "NitroSQLiteException.hpp"
4
+ #include <filesystem>
5
+ #include <memory>
6
+ #include <stdexcept>
7
+ #include <unordered_map>
8
+ #include <vector>
9
+
10
+ namespace margelo::nitro::rnnitrosqlite {
11
+
12
+ namespace fs = std::filesystem;
13
+
14
+ namespace {
15
+
16
+ constexpr const char* kIndependentPrefix = "nitro-sqlite:";
17
+
18
+ int readOnlyAuthorizer(void*, int action, const char*, const char*, const char*, const char*) {
19
+ return action == SQLITE_ATTACH ? SQLITE_DENY : SQLITE_OK;
20
+ }
21
+
22
+ struct IndependentId {
23
+ fs::path physicalPath;
24
+ bool readOnly;
25
+ };
26
+
27
+ std::optional<IndependentId> parseIndependentId(const std::string& key) {
28
+ if (key.empty() || key.front() != '\0' || key.compare(1, std::char_traits<char>::length(kIndependentPrefix), kIndependentPrefix) != 0) {
29
+ return std::nullopt;
30
+ }
31
+ const auto separator = key.find('\0', 1);
32
+ if (separator == std::string::npos || separator + 2 >= key.size()) {
33
+ return std::nullopt;
34
+ }
35
+ const char mode = key[separator + 1];
36
+ if (mode != 'r' && mode != 'w') {
37
+ return std::nullopt;
38
+ }
39
+ return IndependentId{fs::path(key.substr(separator + 2)), mode == 'r'};
40
+ }
41
+
42
+ std::string connectionLabel(const std::string& key) {
43
+ const auto independent = parseIndependentId(key);
44
+ if (independent) {
45
+ return independent->physicalPath.string();
46
+ }
47
+ return key.find('\0') == std::string::npos ? key : "invalid connection ID";
48
+ }
49
+
50
+ template <typename Predicate>
51
+ bool anyDatabasePath(sqlite3* database, Predicate&& predicate) {
52
+ sqlite3_stmt* rawStatement = nullptr;
53
+ const int prepared = sqlite3_prepare_v2(database, "PRAGMA database_list", -1, &rawStatement, nullptr);
54
+ std::unique_ptr<sqlite3_stmt, decltype(&sqlite3_finalize)> statement(rawStatement, sqlite3_finalize);
55
+ if (prepared != SQLITE_OK) {
56
+ throw NitroSQLiteException::SqlExecution(sqlite3_errmsg(database));
57
+ }
58
+ int result;
59
+ while ((result = sqlite3_step(statement.get())) == SQLITE_ROW) {
60
+ const auto* rawPath = reinterpret_cast<const char*>(sqlite3_column_text(statement.get(), 2));
61
+ if (rawPath != nullptr && rawPath[0] != '\0' && predicate(fs::path(rawPath))) {
62
+ return true;
63
+ }
64
+ }
65
+ if (result != SQLITE_DONE) {
66
+ throw NitroSQLiteException::SqlExecution(sqlite3_errmsg(database));
67
+ }
68
+ return false;
69
+ }
70
+
71
+ bool sameFile(const fs::path& first, const fs::path& second) {
72
+ std::error_code error;
73
+ if (fs::equivalent(first, second, error) && !error) {
74
+ return true;
75
+ }
76
+ return canonicalDatabasePath(first) == canonicalDatabasePath(second);
77
+ }
78
+
79
+ } // namespace
80
+
81
+ SQLiteConnection::SQLiteConnection(std::string connectionName, fs::path path, bool isReadOnly, sqlite3* handle)
82
+ : name(std::move(connectionName)), physicalPath(canonicalDatabasePath(path)), readOnly(isReadOnly), database(handle) {}
83
+
84
+ SQLiteConnection::~SQLiteConnection() {
85
+ close();
86
+ }
87
+
88
+ void SQLiteConnection::close() noexcept {
89
+ std::lock_guard lock(mutex);
90
+ if (database == nullptr) {
91
+ return;
92
+ }
93
+ sqlite3_close_v2(database);
94
+ database = nullptr;
95
+ }
96
+
97
+ void DatabaseConnections::open(const std::string& key, const fs::path& path, bool readOnly) {
98
+ std::lock_guard lock(lifecycleMutex);
99
+ validateDatabaseName(key);
100
+ if (connections.contains(key)) {
101
+ throw NitroSQLiteException::DatabaseAlreadyOpen(key);
102
+ }
103
+ openKey(key, path, readOnly);
104
+ }
105
+
106
+ std::string DatabaseConnections::openIndependent(const fs::path& path, bool readOnly) {
107
+ std::lock_guard lock(lifecycleMutex);
108
+ if (path.string().find('\0') != std::string::npos) {
109
+ throw NitroSQLiteException(NitroSQLiteExceptionType::DatabaseCannotBeOpened, "Database path contains a NUL byte");
110
+ }
111
+ if (sqlite3_threadsafe() == 0) {
112
+ throw NitroSQLiteException(NitroSQLiteExceptionType::DatabaseCannotBeOpened,
113
+ "Independent connections require a thread-safe SQLite build");
114
+ }
115
+ // NUL cannot occur in a filesystem name. The ID cannot collide with a legacy database key.
116
+ const auto physicalPath = canonicalDatabasePath(path);
117
+ const std::string key = std::string(1, '\0') + kIndependentPrefix + std::to_string(++nextConnectionId) + std::string(1, '\0') +
118
+ (readOnly ? "r" : "w") + physicalPath.string();
119
+ openKey(key, path, readOnly);
120
+ return key;
121
+ }
122
+
123
+ void DatabaseConnections::openKey(const std::string& key, const fs::path& path, bool readOnly) {
124
+ if (path.string().find('\0') != std::string::npos) {
125
+ throw NitroSQLiteException(NitroSQLiteExceptionType::DatabaseCannotBeOpened, "Database path contains a NUL byte");
126
+ }
127
+ const auto physicalPath = canonicalDatabasePath(path);
128
+ const int flags = (readOnly ? SQLITE_OPEN_READONLY : SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE) | SQLITE_OPEN_FULLMUTEX;
129
+ sqlite3* rawDatabase = nullptr;
130
+ const int result = sqlite3_open_v2(physicalPath.string().c_str(), &rawDatabase, flags, nullptr);
131
+ std::unique_ptr<sqlite3, decltype(&sqlite3_close_v2)> database(rawDatabase, sqlite3_close_v2);
132
+ if (result != SQLITE_OK) {
133
+ const std::string message = rawDatabase == nullptr ? sqlite3_errstr(result) : sqlite3_errmsg(rawDatabase);
134
+ throw NitroSQLiteException(NitroSQLiteExceptionType::DatabaseCannotBeOpened, message);
135
+ }
136
+ if (readOnly) {
137
+ sqlite3_set_authorizer(database.get(), readOnlyAuthorizer, nullptr);
138
+ }
139
+ auto connection = std::make_shared<SQLiteConnection>(connectionLabel(key), physicalPath, readOnly, database.get());
140
+ database.release();
141
+ connections.emplace(key, std::move(connection));
142
+ }
143
+
144
+ void DatabaseConnections::close(const std::string& key) {
145
+ std::lock_guard lock(lifecycleMutex);
146
+ const auto found = connections.find(key);
147
+ if (found == connections.end()) {
148
+ throw NitroSQLiteException::DatabaseNotOpen(connectionLabel(key));
149
+ }
150
+ auto connection = std::move(found->second);
151
+ connections.erase(found);
152
+ connection->close();
153
+ }
154
+
155
+ void DatabaseConnections::closeAll() {
156
+ std::lock_guard lock(lifecycleMutex);
157
+ for (auto& [key, connection] : connections) {
158
+ connection->close();
159
+ }
160
+ connections.clear();
161
+ }
162
+
163
+ SQLiteConnectionPtr DatabaseConnections::get(const std::string& key) {
164
+ std::lock_guard lock(lifecycleMutex);
165
+ const auto found = connections.find(key);
166
+ if (found == connections.end()) {
167
+ throw NitroSQLiteException::DatabaseNotOpen(connectionLabel(key));
168
+ }
169
+ return found->second;
170
+ }
171
+
172
+ bool DatabaseConnections::isOpen(const std::string& key) {
173
+ std::lock_guard lock(lifecycleMutex);
174
+ return connections.contains(key);
175
+ }
176
+
177
+ std::optional<fs::path> DatabaseConnections::physicalPathForKey(const std::string& key) {
178
+ std::lock_guard lock(lifecycleMutex);
179
+ const auto live = connections.find(key);
180
+ if (live != connections.end()) {
181
+ return live->second->physicalPath;
182
+ }
183
+ const auto independent = parseIndependentId(key);
184
+ return independent ? std::optional<fs::path>(independent->physicalPath) : std::nullopt;
185
+ }
186
+
187
+ std::optional<fs::path> DatabaseConnections::findLivePath(const fs::path& first, const fs::path& second) {
188
+ std::optional<fs::path> found;
189
+ withConnectionsLocked([&]() {
190
+ for (const auto& [_, connection] : connections) {
191
+ if (connection->database == nullptr) {
192
+ continue;
193
+ }
194
+ anyDatabasePath(connection->database, [&](const fs::path& candidate) {
195
+ if (sameFile(candidate, first)) {
196
+ found = first;
197
+ return true;
198
+ }
199
+ if (sameFile(candidate, second)) {
200
+ found = second;
201
+ return true;
202
+ }
203
+ return false;
204
+ });
205
+ if (found) {
206
+ return;
207
+ }
208
+ }
209
+ });
210
+ return found;
211
+ }
212
+
213
+ void DatabaseConnections::withConnectionsLocked(const std::function<void()>& action) {
214
+ std::lock_guard lifecycleLock(lifecycleMutex);
215
+ std::vector<std::unique_lock<std::recursive_mutex>> locks;
216
+ locks.reserve(connections.size());
217
+ for (const auto& [_, connection] : connections) {
218
+ locks.emplace_back(connection->mutex);
219
+ }
220
+ action();
221
+ }
222
+
223
+ void DatabaseConnections::drop(const std::string& dbName, const fs::path& path, const std::optional<std::string>& connectionId,
224
+ const std::optional<fs::path>& otherPath) {
225
+ std::lock_guard lifecycleLock(lifecycleMutex);
226
+ validateDatabaseName(dbName);
227
+ const auto target = canonicalDatabasePath(path);
228
+ if (!fs::exists(target)) {
229
+ throw NitroSQLiteException::DatabaseFileNotFound(target.string());
230
+ }
231
+ const auto key = connectionId.value_or(dbName);
232
+ const auto live = connections.find(key);
233
+ const auto independent = connectionId ? parseIndependentId(*connectionId) : std::nullopt;
234
+ if (connectionId && !independent) {
235
+ throw NitroSQLiteException::DatabaseNotOpen(connectionLabel(key));
236
+ }
237
+ if (independent && (!sameFile(independent->physicalPath, target) || independent->readOnly)) {
238
+ throw NitroSQLiteException(NitroSQLiteExceptionType::SqlExecutionError, independent->readOnly
239
+ ? "A read-only connection cannot delete a database"
240
+ : "Connection targets a different database file");
241
+ }
242
+ if (live != connections.end()) {
243
+ if (!sameFile(live->second->physicalPath, target)) {
244
+ throw NitroSQLiteException(NitroSQLiteExceptionType::SqlExecutionError, "Connection targets a different database file");
245
+ }
246
+ if (live->second->readOnly) {
247
+ throw NitroSQLiteException(NitroSQLiteExceptionType::SqlExecutionError, "A read-only connection cannot delete a database");
248
+ }
249
+ }
250
+
251
+ const SQLiteConnectionPtr connectionToClose = live == connections.end() ? nullptr : live->second;
252
+ withConnectionsLocked([&]() {
253
+ if (isPathInUse(target, key) || (otherPath && isPathInUse(*otherPath, key))) {
254
+ throw NitroSQLiteException(NitroSQLiteExceptionType::SqlExecutionError, "Database is in use by another connection");
255
+ }
256
+ if (!fs::exists(target)) {
257
+ throw NitroSQLiteException::DatabaseFileNotFound(target.string());
258
+ }
259
+ if (connectionToClose) {
260
+ close(key);
261
+ }
262
+ if (!removeDatabaseFiles(target.filename().string(), target.parent_path())) {
263
+ throw NitroSQLiteException(NitroSQLiteExceptionType::SqlExecutionError, "Could not remove database files");
264
+ }
265
+ if (otherPath) {
266
+ removeDatabaseFiles(otherPath->filename().string(), otherPath->parent_path());
267
+ }
268
+ });
269
+ }
270
+
271
+ bool DatabaseConnections::isPathInUse(const fs::path& path, const std::string& excludedKey) const {
272
+ for (const auto& [key, connection] : connections) {
273
+ if (key == excludedKey) {
274
+ continue;
275
+ }
276
+ if (connection->database == nullptr) {
277
+ continue;
278
+ }
279
+ if (anyDatabasePath(connection->database, [&](const fs::path& candidate) { return sameFile(candidate, path); })) {
280
+ return true;
281
+ }
282
+ }
283
+ return false;
284
+ }
285
+
286
+ DatabaseConnections& databaseConnections() {
287
+ static DatabaseConnections registry;
288
+ return registry;
289
+ }
290
+
291
+ void validateDatabaseName(const std::string& dbName) {
292
+ if (dbName.find('\0') != std::string::npos) {
293
+ throw NitroSQLiteException(NitroSQLiteExceptionType::DatabaseCannotBeOpened, "Database name contains a NUL byte");
294
+ }
295
+ }
296
+
297
+ fs::path canonicalDatabasePath(const fs::path& path) {
298
+ std::error_code error;
299
+ auto canonical = fs::weakly_canonical(path, error);
300
+ if (!error) {
301
+ return canonical;
302
+ }
303
+ return fs::absolute(path).lexically_normal();
304
+ }
305
+
306
+ } // namespace margelo::nitro::rnnitrosqlite
@@ -0,0 +1,86 @@
1
+ #pragma once
2
+
3
+ #include <filesystem>
4
+ #include <functional>
5
+ #include <map>
6
+ #include <memory>
7
+ #include <mutex>
8
+ #include <optional>
9
+ #include <queue>
10
+ #include <sqlite3.h>
11
+ #include <string>
12
+
13
+ namespace margelo::nitro::rnnitrosqlite {
14
+
15
+ /** One native SQLite handle, its file identity, and its operation locks. */
16
+ struct SQLiteConnection final : std::enable_shared_from_this<SQLiteConnection> {
17
+ SQLiteConnection(std::string name, std::filesystem::path physicalPath, bool readOnly, sqlite3* database);
18
+ ~SQLiteConnection();
19
+
20
+ SQLiteConnection(const SQLiteConnection&) = delete;
21
+ SQLiteConnection& operator=(const SQLiteConnection&) = delete;
22
+
23
+ /** Close the native handle. References to this connection may remain alive. */
24
+ void close() noexcept;
25
+ /** Submit an operation to this connection's native FIFO worker. */
26
+ void enqueueAsync(std::function<void()> operation);
27
+
28
+ const std::string name;
29
+ const std::filesystem::path physicalPath;
30
+ const bool readOnly;
31
+ sqlite3* database;
32
+ std::recursive_mutex mutex;
33
+
34
+ private:
35
+ void drainAsync();
36
+
37
+ std::mutex asyncQueueMutex;
38
+ std::queue<std::function<void()>> asyncQueue;
39
+ bool asyncWorkerRunning = false;
40
+ };
41
+
42
+ /** Shared ownership of a native connection across pending operations. */
43
+ using SQLiteConnectionPtr = std::shared_ptr<SQLiteConnection>;
44
+
45
+ /** Registry of default database names and opaque independent connection IDs. */
46
+ class DatabaseConnections final {
47
+ public:
48
+ // Callers hold this while resolving or migrating a database path. It is recursive because
49
+ // open, attach and drop take it again after path resolution.
50
+ std::recursive_mutex lifecycleMutex;
51
+
52
+ /** Open a name-based default connection. An existing key is an error. */
53
+ void open(const std::string& key, const std::filesystem::path& path, bool readOnly);
54
+ /** Open a separate handle to @p path and return its opaque connection ID. */
55
+ std::string openIndependent(const std::filesystem::path& path, bool readOnly);
56
+ /** Close the handle identified by a default name or independent ID. */
57
+ void close(const std::string& key);
58
+ /** Close all registered handles. */
59
+ void closeAll();
60
+ /** Return a live connection or throw if @p key is unknown. */
61
+ SQLiteConnectionPtr get(const std::string& key);
62
+ /** Check whether @p key still identifies an open connection. */
63
+ bool isOpen(const std::string& key);
64
+ /** Resolve the file path for a registered or encoded independent key. */
65
+ std::optional<std::filesystem::path> physicalPathForKey(const std::string& key);
66
+ /** Find an open connection using either candidate path. */
67
+ std::optional<std::filesystem::path> findLivePath(const std::filesystem::path& first, const std::filesystem::path& second);
68
+ /** Run @p action while holding the lifecycle and every connection lock. */
69
+ void withConnectionsLocked(const std::function<void()>& action);
70
+ /** Delete a database after checking that no other connection or attachment uses it. */
71
+ void drop(const std::string& dbName, const std::filesystem::path& path, const std::optional<std::string>& connectionId,
72
+ const std::optional<std::filesystem::path>& otherPath = std::nullopt);
73
+
74
+ private:
75
+ void openKey(const std::string& key, const std::filesystem::path& path, bool readOnly);
76
+ bool isPathInUse(const std::filesystem::path& path, const std::string& excludedKey) const;
77
+
78
+ std::map<std::string, SQLiteConnectionPtr> connections;
79
+ unsigned long long nextConnectionId = 0;
80
+ };
81
+
82
+ DatabaseConnections& databaseConnections();
83
+ std::filesystem::path canonicalDatabasePath(const std::filesystem::path& path);
84
+ void validateDatabaseName(const std::string& dbName);
85
+
86
+ } // namespace margelo::nitro::rnnitrosqlite
@@ -1,5 +1,5 @@
1
- #include "databaseMigration.hpp"
2
- #include "logs.hpp"
1
+ #include "NitroSQLiteDatabaseMigration.hpp"
2
+ #include "NitroSQLiteLogs.hpp"
3
3
  #include <array>
4
4
  #include <system_error>
5
5
 
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * SQL Batch execution implementation using default sqliteBridge implementation
3
3
  */
4
- #include "sqliteExecuteBatch.hpp"
4
+ #include "NitroSQLiteExecuteBatch.hpp"
5
5
  #include "NitroSQLiteException.hpp"
6
- #include "operations.hpp"
6
+ #include "NitroSQLiteOperations.hpp"
7
7
  #include <utility>
8
8
 
9
- namespace margelo::rnnitrosqlite {
9
+ namespace margelo::nitro::rnnitrosqlite {
10
10
 
11
11
  std::vector<BatchQuery> batchParamsToCommands(const std::vector<BatchQueryCommand>& batchParams) {
12
12
  auto commands = std::vector<BatchQuery>();
@@ -67,4 +67,4 @@ SQLiteOperationResult sqliteExecuteBatch(const SQLiteConnectionPtr& connection,
67
67
  }
68
68
  }
69
69
 
70
- } // namespace margelo::rnnitrosqlite
70
+ } // namespace margelo::nitro::rnnitrosqlite
@@ -4,13 +4,10 @@
4
4
  #pragma once
5
5
 
6
6
  #include "BatchQueryCommand.hpp"
7
- #include "types.hpp"
7
+ #include "NitroSQLiteTypes.hpp"
8
8
  #include <memory>
9
9
 
10
- using namespace facebook;
11
- using namespace margelo::nitro;
12
-
13
- namespace margelo::rnnitrosqlite {
10
+ namespace margelo::nitro::rnnitrosqlite {
14
11
 
15
12
  struct SQLiteConnection;
16
13
 
@@ -31,4 +28,4 @@ std::vector<BatchQuery> batchParamsToCommands(const std::vector<BatchQueryComman
31
28
  SQLiteOperationResult sqliteExecuteBatch(const std::string& dbName, const std::vector<BatchQuery>& commands);
32
29
  SQLiteOperationResult sqliteExecuteBatch(const std::shared_ptr<SQLiteConnection>& connection, const std::vector<BatchQuery>& commands);
33
30
 
34
- } // namespace margelo::rnnitrosqlite
31
+ } // namespace margelo::nitro::rnnitrosqlite
@@ -2,13 +2,13 @@
2
2
  * SQL File Loader implementation
3
3
  */
4
4
 
5
- #include "importSqlFile.hpp"
5
+ #include "NitroSQLiteImportSqlFile.hpp"
6
6
  #include "NitroSQLiteException.hpp"
7
- #include "operations.hpp"
7
+ #include "NitroSQLiteOperations.hpp"
8
8
  #include <fstream>
9
9
  #include <iostream>
10
10
 
11
- namespace margelo::rnnitrosqlite {
11
+ namespace margelo::nitro::rnnitrosqlite {
12
12
 
13
13
  SQLiteOperationResult importSqlFile(const std::string& dbName, const std::string& fileLocation) {
14
14
  return importSqlFile(sqliteGetOpenDatabase(dbName), fileLocation);
@@ -50,4 +50,4 @@ SQLiteOperationResult importSqlFile(const SQLiteConnectionPtr& connection, const
50
50
  }
51
51
  }
52
52
 
53
- } // namespace margelo::rnnitrosqlite
53
+ } // namespace margelo::nitro::rnnitrosqlite
@@ -6,14 +6,14 @@
6
6
 
7
7
  #pragma once
8
8
 
9
- #include "types.hpp"
9
+ #include "NitroSQLiteTypes.hpp"
10
10
  #include <memory>
11
11
 
12
- namespace margelo::rnnitrosqlite {
12
+ namespace margelo::nitro::rnnitrosqlite {
13
13
 
14
14
  struct SQLiteConnection;
15
15
 
16
16
  SQLiteOperationResult importSqlFile(const std::string& dbName, const std::string& fileLocation);
17
17
  SQLiteOperationResult importSqlFile(const std::shared_ptr<SQLiteConnection>& connection, const std::string& fileLocation);
18
18
 
19
- } // namespace margelo::rnnitrosqlite
19
+ } // namespace margelo::nitro::rnnitrosqlite
@@ -1,5 +1,5 @@
1
- #ifndef macros_h
2
- #define macros_h
1
+ #ifndef NITRO_SQLITE_MACROS_HPP
2
+ #define NITRO_SQLITE_MACROS_HPP
3
3
 
4
4
  #define HOSTFN(name, basecount) \
5
5
  jsi::Function::createFromHostFunction( \
@@ -10,4 +10,4 @@ basecount, \
10
10
 
11
11
  #define JSIFN(capture) capture(jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count)->jsi::Value
12
12
 
13
- #endif /* macros_h */
13
+ #endif /* NITRO_SQLITE_MACROS_HPP */