react-native-nitro-sqlite 9.6.0 → 9.8.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 (68) hide show
  1. package/RNNitroSQLite.podspec +56 -20
  2. package/android/CMakeLists.txt +15 -0
  3. package/android/build.gradle +14 -0
  4. package/cpp/NitroSQLiteException.hpp +5 -0
  5. package/cpp/databaseMigration.cpp +123 -0
  6. package/cpp/databaseMigration.hpp +13 -0
  7. package/cpp/hybridObjects/HybridNitroSQLite.cpp +65 -16
  8. package/cpp/hybridObjects/HybridNitroSQLite.hpp +5 -0
  9. package/cpp/hybridObjects/HybridNitroSQLiteQueryResult.cpp +5 -4
  10. package/cpp/hybridObjects/HybridNitroSQLiteQueryResult.hpp +1 -1
  11. package/cpp/importSqlFile.cpp +10 -5
  12. package/cpp/importSqlFile.hpp +5 -1
  13. package/cpp/operations.cpp +232 -168
  14. package/cpp/operations.hpp +31 -1
  15. package/cpp/sqliteExecuteBatch.cpp +17 -16
  16. package/cpp/sqliteExecuteBatch.hpp +4 -0
  17. package/ios/OnLoad.mm +26 -3
  18. package/lib/commonjs/DatabaseQueue.js +4 -17
  19. package/lib/commonjs/DatabaseQueue.js.map +1 -1
  20. package/lib/commonjs/operations/execute.js +28 -11
  21. package/lib/commonjs/operations/execute.js.map +1 -1
  22. package/lib/commonjs/operations/session.js +34 -9
  23. package/lib/commonjs/operations/session.js.map +1 -1
  24. package/lib/commonjs/operations/transaction.js +5 -5
  25. package/lib/commonjs/operations/transaction.js.map +1 -1
  26. package/lib/module/DatabaseQueue.js +4 -15
  27. package/lib/module/DatabaseQueue.js.map +1 -1
  28. package/lib/module/operations/execute.js +24 -12
  29. package/lib/module/operations/execute.js.map +1 -1
  30. package/lib/module/operations/session.js +36 -11
  31. package/lib/module/operations/session.js.map +1 -1
  32. package/lib/module/operations/transaction.js +6 -6
  33. package/lib/module/operations/transaction.js.map +1 -1
  34. package/lib/typescript/commonjs/DatabaseQueue.d.ts +1 -3
  35. package/lib/typescript/commonjs/DatabaseQueue.d.ts.map +1 -1
  36. package/lib/typescript/commonjs/operations/execute.d.ts +6 -0
  37. package/lib/typescript/commonjs/operations/execute.d.ts.map +1 -1
  38. package/lib/typescript/commonjs/operations/session.d.ts.map +1 -1
  39. package/lib/typescript/commonjs/specs/NitroSQLite.nitro.d.ts.map +1 -1
  40. package/lib/typescript/commonjs/specs/NitroSQLiteQueryResult.nitro.d.ts.map +1 -1
  41. package/lib/typescript/module/DatabaseQueue.d.ts +1 -3
  42. package/lib/typescript/module/DatabaseQueue.d.ts.map +1 -1
  43. package/lib/typescript/module/operations/execute.d.ts +6 -0
  44. package/lib/typescript/module/operations/execute.d.ts.map +1 -1
  45. package/lib/typescript/module/operations/session.d.ts.map +1 -1
  46. package/lib/typescript/module/specs/NitroSQLite.nitro.d.ts.map +1 -1
  47. package/lib/typescript/module/specs/NitroSQLiteQueryResult.nitro.d.ts.map +1 -1
  48. package/nitrogen/generated/android/RNNitroSQLiteOnLoad.cpp +2 -2
  49. package/nitrogen/generated/android/c++/JHybridNitroSQLiteOnLoadSpec.hpp +2 -2
  50. package/nitrogen/generated/android/kotlin/com/margelo/nitro/rnnitrosqlite/HybridNitroSQLiteOnLoadSpec.kt +2 -0
  51. package/nitrogen/generated/ios/RNNitroSQLite+autolinking.rb +2 -0
  52. package/package.json +9 -7
  53. package/src/DatabaseQueue.ts +9 -19
  54. package/src/operations/execute.ts +52 -12
  55. package/src/operations/session.ts +57 -11
  56. package/src/operations/transaction.ts +6 -6
  57. package/src/specs/NitroSQLite.nitro.ts +4 -1
  58. package/src/specs/NitroSQLiteQueryResult.nitro.ts +4 -1
  59. package/android/.gradle/8.9/checksums/checksums.lock +0 -0
  60. package/android/.gradle/8.9/checksums/md5-checksums.bin +0 -0
  61. package/android/.gradle/8.9/checksums/sha1-checksums.bin +0 -0
  62. package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
  63. package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
  64. package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
  65. package/android/.gradle/8.9/gc.properties +0 -0
  66. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  67. package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
  68. package/android/.gradle/vcs-1/gc.properties +0 -0
@@ -1,11 +1,48 @@
1
1
  require "json"
2
2
 
3
3
  package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
- folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1'
4
+ app_package_json_path = File.expand_path("../package.json", Pod::Config.instance.installation_root)
5
+ app_package = File.exist?(app_package_json_path) ? JSON.parse(File.read(app_package_json_path)) : {}
6
+ app_config = app_package.fetch("nitroSQLite", {})
7
+
8
+ unless app_config.is_a?(Hash)
9
+ raise "nitroSQLite in package.json must be an object"
10
+ end
11
+
12
+ if ENV.key?("NITRO_SQLITE_THREADSAFE")
13
+ thread_safe_value = ENV["NITRO_SQLITE_THREADSAFE"]
14
+ unless %w[true false 1 0].include?(thread_safe_value)
15
+ raise "NITRO_SQLITE_THREADSAFE must be true, false, 1, or 0"
16
+ end
17
+
18
+ sqlite_threadsafe = %w[true 1].include?(thread_safe_value) ? "1" : "0"
19
+ else
20
+ thread_safe_value = app_config.fetch("threadSafe", true)
21
+ unless [true, false].include?(thread_safe_value)
22
+ raise "nitroSQLite.threadSafe in package.json must be true or false"
23
+ end
24
+
25
+ sqlite_threadsafe = thread_safe_value ? "1" : "0"
26
+ end
27
+
28
+ if ENV.key?("NITRO_SQLITE_PERFORMANCE_MODE")
29
+ performance_mode_value = ENV["NITRO_SQLITE_PERFORMANCE_MODE"]
30
+ unless %w[true false 1 0].include?(performance_mode_value)
31
+ raise "NITRO_SQLITE_PERFORMANCE_MODE must be true, false, 1, or 0"
32
+ end
33
+
34
+ performance_mode = %w[true 1].include?(performance_mode_value)
35
+ else
36
+ performance_mode = app_config.fetch("performanceMode", true)
5
37
 
6
- # TODO: Should be customizable in package.json.
7
- # Used to create comparable benchmark results
8
- performance_mode = 1
38
+ unless [true, false].include?(performance_mode)
39
+ raise "nitroSQLite.performanceMode in package.json must be true or false"
40
+ end
41
+ end
42
+ folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1'
43
+ log_message = lambda do |message|
44
+ puts "\e[34m#{message}\e[0m"
45
+ end
9
46
 
10
47
  Pod::Spec.new do |s|
11
48
  s.name = "RNNitroSQLite"
@@ -17,6 +54,10 @@ Pod::Spec.new do |s|
17
54
  s.platforms = { :ios => min_ios_version_supported, :visionos => "1.0" }
18
55
  s.source = { :git => "https://github.com/margelo/react-native-nitro-sqlite.git", :tag => "#{s.version}" }
19
56
 
57
+ # Opt-in vector search (NITRO_SQLITE_VEC=1); the companion pod compiles the sources.
58
+ nitro_sqlite_vec = ENV['NITRO_SQLITE_VEC'] == '1'
59
+ nitro_sqlite_vec_cpp = File.expand_path(File.join(__dir__, "..", "react-native-nitro-sqlite-vec", "cpp"))
60
+
20
61
  s.source_files = [
21
62
  # Implementation (Swift)
22
63
  "ios/**/*.{swift}",
@@ -26,17 +67,24 @@ Pod::Spec.new do |s|
26
67
  "cpp/**/*.{h,hpp,c,cpp}"
27
68
  ]
28
69
 
70
+ inherited_cflags = '$(inherited)'
71
+ optimized_cflags = '-DSQLITE_DQS=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS=1 -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_OMIT_DEPRECATED=1 -DSQLITE_OMIT_PROGRESS_CALLBACK=1 -DSQLITE_OMIT_SHARED_CACHE=1 -DSQLITE_USE_ALLOCA=1'
72
+
73
+ log_message.call("SQLite thread safety: SQLITE_THREADSAFE=#{sqlite_threadsafe}")
74
+ log_message.call("SQLite performance mode: #{performance_mode ? "enabled" : "disabled"}")
75
+ performance_cflags = performance_mode ? " #{optimized_cflags}" : ""
76
+ other_cflags = "#{inherited_cflags}#{performance_cflags} -DSQLITE_THREADSAFE=#{sqlite_threadsafe} "
77
+
29
78
  s.pod_target_xcconfig = {
30
79
  :GCC_PREPROCESSOR_DEFINITIONS => "HAVE_FULLFSYNC=1",
31
80
  :WARNING_CFLAGS => "-Wno-shorten-64-to-32 -Wno-comma -Wno-unreachable-code -Wno-conditional-uninitialized -Wno-deprecated-declarations",
32
81
  'CLANG_CXX_LANGUAGE_STANDARD' => 'c++20',
33
82
  'CLANG_CXX_LIBRARY' => 'libc++',
34
83
  'DEFINES_MODULE' => 'YES',
35
- "HEADER_SEARCH_PATHS" => [
36
- "${PODS_ROOT}/RCT-Folly",
37
- ],
38
- "GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) FOLLY_NO_CONFIG FOLLY_CFG_NO_COROUTINES",
84
+ "HEADER_SEARCH_PATHS" => "\"${PODS_ROOT}/RCT-Folly\"" + (nitro_sqlite_vec ? " \"#{nitro_sqlite_vec_cpp}\"" : ""),
85
+ "GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) FOLLY_NO_CONFIG FOLLY_CFG_NO_COROUTINES" + (nitro_sqlite_vec ? " NITRO_SQLITE_VEC=1" : ""),
39
86
  "OTHER_CPLUSPLUSFLAGS" => folly_compiler_flags,
87
+ "OTHER_CFLAGS" => other_cflags,
40
88
  }
41
89
 
42
90
  load 'nitrogen/generated/ios/RNNitroSQLite+autolinking.rb'
@@ -44,18 +92,6 @@ Pod::Spec.new do |s|
44
92
 
45
93
  install_modules_dependencies(s)
46
94
 
47
- optimizedCflags = '$(inherited) -DSQLITE_DQS=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS=1 -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_OMIT_DEPRECATED=1 -DSQLITE_OMIT_PROGRESS_CALLBACK=1 -DSQLITE_OMIT_SHARED_CACHE=1 -DSQLITE_USE_ALLOCA=1'
48
-
49
- if performance_mode == '1' then
50
- log_message.call("Thread unsafe (1) performance mode enabled. Use only transactions! 🚀🚀")
51
- xcconfig[:OTHER_CFLAGS] = optimizedCflags + ' -DSQLITE_THREADSAFE=0 '
52
- end
53
-
54
- if performance_mode == '2' then
55
- log_message.call("Thread safe (2) performance mode enabled 🚀")
56
- xcconfig[:OTHER_CFLAGS] = optimizedCflags + ' -DSQLITE_THREADSAFE=1 '
57
- end
58
-
59
95
  if ENV['NITRO_SQLITE_USE_PHONE_VERSION'] == '1' then
60
96
  s.exclude_files = "cpp/sqlite/sqlite3.c", "cpp/sqlite/sqlite3.h"
61
97
  s.library = "sqlite3"
@@ -29,6 +29,21 @@ include_directories(
29
29
  src/main/cpp
30
30
  )
31
31
 
32
+ # Opt-in: compile the companion's sqlite-vec into this library (shared single sqlite3).
33
+ if(NITRO_SQLITE_VEC)
34
+ message(STATUS "[react-native-nitro-sqlite] vector search ENABLED (sqlite-vec from ${NITRO_SQLITE_VEC_CPP_DIR})")
35
+ set(NITRO_SQLITE_VEC_SOURCES
36
+ "${NITRO_SQLITE_VEC_CPP_DIR}/sqlite-vec/sqlite-vec.c"
37
+ "${NITRO_SQLITE_VEC_CPP_DIR}/registerVectorExtensions.cpp"
38
+ )
39
+ target_sources(${CMAKE_PROJECT_NAME} PRIVATE ${NITRO_SQLITE_VEC_SOURCES})
40
+ # SQLITE_CORE: link against our bundled sqlite3; SQLITE_VEC_STATIC: drop Windows dllexport.
41
+ set_source_files_properties(${NITRO_SQLITE_VEC_SOURCES}
42
+ PROPERTIES COMPILE_DEFINITIONS "SQLITE_CORE;SQLITE_VEC_STATIC")
43
+ target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE "${NITRO_SQLITE_VEC_CPP_DIR}")
44
+ target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE NITRO_SQLITE_VEC)
45
+ endif()
46
+
32
47
  set_target_properties(
33
48
  ${CMAKE_PROJECT_NAME} PROPERTIES
34
49
  CXX_STANDARD ${CMAKE_CXX_STANDARD}
@@ -75,6 +75,18 @@ def REACT_NATIVE_MINOR_VERSION = REACT_NATIVE_VERSION.startsWith("0.0.0-") ? 100
75
75
 
76
76
  def SQLITE_FLAGS = rootProject.properties['nitroSqliteFlags']
77
77
 
78
+ // Opt-in: resolve the companion's cpp dir so CMake compiles its sqlite-vec into this library.
79
+ def NITRO_SQLITE_VEC_ENABLED = (rootProject.findProperty('nitroSqliteVec') ?: 'false').toString() == 'true'
80
+ def NITRO_SQLITE_VEC_CPP_DIR = ''
81
+ if (NITRO_SQLITE_VEC_ENABLED) {
82
+ try {
83
+ def vecPkgJson = ["node", "--print", "require.resolve('react-native-nitro-sqlite-vec/package.json')"].execute(null, rootDir).text.trim()
84
+ NITRO_SQLITE_VEC_CPP_DIR = new File(new File(vecPkgJson).parentFile, "cpp").absolutePath
85
+ } catch (Exception e) {
86
+ throw new GradleException("[react-native-nitro-sqlite] nitroSqliteVec=true but 'react-native-nitro-sqlite-vec' could not be resolved. Install it in your app (e.g. `bun add react-native-nitro-sqlite-vec`).")
87
+ }
88
+ }
89
+
78
90
  apply plugin: "com.android.library"
79
91
  apply plugin: "kotlin-android"
80
92
 
@@ -111,6 +123,8 @@ android {
111
123
  "-DANDROID_TOOLCHAIN=clang",
112
124
  "-DREACT_NATIVE_MINOR_VERSION=${REACT_NATIVE_MINOR_VERSION}",
113
125
  "-DSQLITE_FLAGS='${SQLITE_FLAGS ? SQLITE_FLAGS : ''}'",
126
+ "-DNITRO_SQLITE_VEC=${NITRO_SQLITE_VEC_ENABLED ? 'ON' : 'OFF'}",
127
+ "-DNITRO_SQLITE_VEC_CPP_DIR=${NITRO_SQLITE_VEC_CPP_DIR}",
114
128
  "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
115
129
  abiFilters (*reactNativeArchitectures())
116
130
  }
@@ -46,6 +46,11 @@ public:
46
46
  return this->_exceptionString.c_str();
47
47
  }
48
48
 
49
+ static NitroSQLiteException DatabaseAlreadyOpen(const std::string& dbName) {
50
+ return NitroSQLiteException(NitroSQLiteExceptionType::DatabaseCannotBeOpened,
51
+ "Database " + dbName + " is already open. There is already a connection to the database.");
52
+ }
53
+
49
54
  static NitroSQLiteException DatabaseNotOpen(const std::string& dbName) {
50
55
  return NitroSQLiteException(NitroSQLiteExceptionType::UnableToAttachToDatabase, dbName + " is not open");
51
56
  }
@@ -0,0 +1,123 @@
1
+ #include "databaseMigration.hpp"
2
+ #include "logs.hpp"
3
+ #include <array>
4
+ #include <system_error>
5
+
6
+ namespace margelo::nitro::rnnitrosqlite {
7
+
8
+ namespace fs = std::filesystem;
9
+
10
+ namespace {
11
+
12
+ constexpr std::size_t kDatabaseFileCount = 4;
13
+ using DatabaseFiles = std::array<std::string, kDatabaseFileCount>;
14
+
15
+ DatabaseFiles getDatabaseFiles(const std::string& dbName);
16
+ bool copyDatabaseFiles(const DatabaseFiles& files, const fs::path& fromDirectory, const fs::path& toDirectory);
17
+ void removeAuxiliaryDatabaseFiles(const DatabaseFiles& files, const fs::path& directory);
18
+
19
+ } // namespace
20
+
21
+ fs::path migrateDatabase(const std::string& dbName, const fs::path& fromDirectory, const fs::path& toDirectory) {
22
+ const auto files = getDatabaseFiles(dbName);
23
+ std::error_code ec;
24
+ const bool sourceExists = fs::exists(fromDirectory / dbName, ec);
25
+
26
+ if (ec) {
27
+ LOGW("Failed to inspect database %s in its old location: %s", dbName.c_str(), ec.message().c_str());
28
+ return fromDirectory;
29
+ }
30
+
31
+ if (!sourceExists) {
32
+ // A completed migration may have been interrupted after deleting the database but before
33
+ // deleting its journals. The destination is already authoritative in that state.
34
+ removeAuxiliaryDatabaseFiles(files, fromDirectory);
35
+ return toDirectory;
36
+ }
37
+
38
+ // A database in the old directory is the live copy. Clear every database generation file at
39
+ // the destination before copying so SQLite never pairs the source with a stale journal.
40
+ if (!removeDatabaseFiles(dbName, toDirectory)) {
41
+ return fromDirectory;
42
+ }
43
+
44
+ fs::create_directories(toDirectory, ec);
45
+ if (ec) {
46
+ LOGW("Failed to create database migration directory %s: %s", toDirectory.string().c_str(), ec.message().c_str());
47
+ return fromDirectory;
48
+ }
49
+
50
+ if (!copyDatabaseFiles(files, fromDirectory, toDirectory)) {
51
+ return fromDirectory;
52
+ }
53
+
54
+ // Delete the database first. If this fails, every source journal must remain beside it so the
55
+ // caller can safely keep using the old location. Leftover journals after a successful database
56
+ // deletion are harmless and are removed on the next migration attempt.
57
+ if (!fs::remove(fromDirectory / dbName, ec) || ec) {
58
+ LOGW("Failed to remove migrated database %s from its old location: %s", dbName.c_str(), ec.message().c_str());
59
+ return fromDirectory;
60
+ }
61
+
62
+ removeAuxiliaryDatabaseFiles(files, fromDirectory);
63
+ return toDirectory;
64
+ }
65
+
66
+ bool removeDatabaseFiles(const std::string& dbName, const fs::path& directory) {
67
+ const auto files = getDatabaseFiles(dbName);
68
+
69
+ for (const auto& file : files) {
70
+ std::error_code ec;
71
+ fs::remove(directory / file, ec);
72
+ if (ec) {
73
+ LOGW("Failed to remove database file %s: %s", file.c_str(), ec.message().c_str());
74
+ return false;
75
+ }
76
+ }
77
+
78
+ return true;
79
+ }
80
+
81
+ namespace {
82
+
83
+ DatabaseFiles getDatabaseFiles(const std::string& dbName) {
84
+ return {dbName, dbName + "-journal", dbName + "-wal", dbName + "-shm"};
85
+ }
86
+
87
+ bool copyDatabaseFiles(const DatabaseFiles& files, const fs::path& fromDirectory, const fs::path& toDirectory) {
88
+ for (const auto& file : files) {
89
+ std::error_code ec;
90
+ const bool sourceExists = fs::exists(fromDirectory / file, ec);
91
+
92
+ if (ec) {
93
+ LOGW("Failed to inspect database file %s: %s", file.c_str(), ec.message().c_str());
94
+ return false;
95
+ }
96
+
97
+ if (!sourceExists) {
98
+ continue;
99
+ }
100
+
101
+ if (!fs::copy_file(fromDirectory / file, toDirectory / file, ec) || ec) {
102
+ LOGW("Failed to migrate database file %s: %s", file.c_str(), ec.message().c_str());
103
+ return false;
104
+ }
105
+ }
106
+
107
+ return true;
108
+ }
109
+
110
+ void removeAuxiliaryDatabaseFiles(const DatabaseFiles& files, const fs::path& directory) {
111
+ for (std::size_t index = 1; index < files.size(); index++) {
112
+ const auto& file = files[index];
113
+ std::error_code ec;
114
+ fs::remove(directory / file, ec);
115
+ if (ec) {
116
+ LOGW("Failed to remove database file %s: %s", file.c_str(), ec.message().c_str());
117
+ }
118
+ }
119
+ }
120
+
121
+ } // namespace
122
+
123
+ } // namespace margelo::nitro::rnnitrosqlite
@@ -0,0 +1,13 @@
1
+ #pragma once
2
+
3
+ #include <filesystem>
4
+ #include <string>
5
+
6
+ namespace margelo::nitro::rnnitrosqlite {
7
+
8
+ std::filesystem::path migrateDatabase(const std::string& dbName, const std::filesystem::path& fromDirectory,
9
+ const std::filesystem::path& toDirectory);
10
+
11
+ bool removeDatabaseFiles(const std::string& dbName, const std::filesystem::path& directory);
12
+
13
+ } // namespace margelo::nitro::rnnitrosqlite
@@ -1,11 +1,14 @@
1
1
  #include "HybridNitroSQLite.hpp"
2
2
  #include "HybridNitroSQLiteQueryResult.hpp"
3
3
  #include "NitroSQLiteException.hpp"
4
+ #include "databaseMigration.hpp"
4
5
  #include "importSqlFile.hpp"
5
6
  #include "logs.hpp"
6
7
  #include "macros.hpp"
7
8
  #include "operations.hpp"
8
9
  #include "sqliteExecuteBatch.hpp"
10
+ #include <exception>
11
+ #include <filesystem>
9
12
  #include <iostream>
10
13
  #include <map>
11
14
  #include <optional>
@@ -65,8 +68,26 @@ const std::string getDocPath(const std::optional<std::string>& location) {
65
68
  return tempDocPath;
66
69
  }
67
70
 
71
+ const std::string getOldDocPath(const std::optional<std::string>& location) {
72
+ std::string oldDocPath = HybridNitroSQLite::migrationDocPath;
73
+ if (location) {
74
+ oldDocPath = oldDocPath + "/" + *location;
75
+ }
76
+
77
+ return oldDocPath;
78
+ }
79
+
80
+ const std::string getMigratedDocPath(const std::string& dbName, const std::optional<std::string>& location) {
81
+ const auto currentDocPath = getDocPath(location);
82
+ if (HybridNitroSQLite::migrationDocPath.empty()) {
83
+ return currentDocPath;
84
+ }
85
+
86
+ return migrateDatabase(dbName, getOldDocPath(location), currentDocPath).string();
87
+ }
88
+
68
89
  void HybridNitroSQLite::open(const std::string& dbName, const std::optional<std::string>& location) {
69
- const auto docPath = getDocPath(location);
90
+ const auto docPath = getMigratedDocPath(dbName, location);
70
91
  sqliteOpenDb(dbName, docPath);
71
92
  }
72
93
 
@@ -75,18 +96,28 @@ void HybridNitroSQLite::close(const std::string& dbName) {
75
96
  };
76
97
 
77
98
  void HybridNitroSQLite::drop(const std::string& dbName, const std::optional<std::string>& location) {
78
- const auto docPath = getDocPath(location);
79
- sqliteRemoveDb(dbName, docPath);
99
+ const auto currentDocPath = getDocPath(location);
100
+ if (migrationDocPath.empty()) {
101
+ sqliteRemoveDb(dbName, currentDocPath);
102
+ return;
103
+ }
104
+
105
+ const auto oldDocPath = getOldDocPath(location);
106
+ std::error_code ec;
107
+ const bool oldDatabaseExists = std::filesystem::exists(std::filesystem::path(oldDocPath) / dbName, ec);
108
+ if (ec) {
109
+ LOGW("Failed to inspect database %s in its old location: %s", dbName.c_str(), ec.message().c_str());
110
+ }
111
+
112
+ sqliteRemoveDb(dbName, oldDatabaseExists || ec ? oldDocPath : currentDocPath);
113
+ removeDatabaseFiles(dbName, oldDocPath);
114
+ removeDatabaseFiles(dbName, currentDocPath);
80
115
  };
81
116
 
82
117
  void HybridNitroSQLite::attach(const std::string& mainDbName, const std::string& dbNameToAttach, const std::string& alias,
83
118
  const std::optional<std::string>& location) {
84
- std::string tempDocPath = std::string(docPath);
85
- if (location) {
86
- tempDocPath = tempDocPath + "/" + *location;
87
- }
88
-
89
- sqliteAttachDb(mainDbName, tempDocPath, dbNameToAttach, alias);
119
+ const auto attachedDocPath = getMigratedDocPath(dbNameToAttach, location);
120
+ sqliteAttachDb(mainDbName, attachedDocPath, dbNameToAttach, alias);
90
121
  };
91
122
 
92
123
  void HybridNitroSQLite::detach(const std::string& mainDbName, const std::string& alias) {
@@ -101,10 +132,16 @@ std::shared_ptr<HybridNitroSQLiteQueryResultSpec> HybridNitroSQLite::execute(con
101
132
  std::shared_ptr<Promise<std::shared_ptr<HybridNitroSQLiteQueryResultSpec>>>
102
133
  HybridNitroSQLite::executeAsync(const std::string& dbName, const std::string& query, const std::optional<SQLiteQueryParams>& params) {
103
134
  const auto copiedParams = copyArrayBufferParamsForBackground(params);
135
+ SQLiteConnectionPtr connection;
136
+ try {
137
+ connection = sqliteGetOpenDatabase(dbName);
138
+ } catch (...) {
139
+ return Promise<std::shared_ptr<HybridNitroSQLiteQueryResultSpec>>::rejected(std::current_exception());
140
+ }
104
141
 
105
142
  return Promise<std::shared_ptr<HybridNitroSQLiteQueryResultSpec>>::async(
106
- [=, this]() -> std::shared_ptr<HybridNitroSQLiteQueryResultSpec> {
107
- auto result = sqliteExecute(dbName, query, copiedParams);
143
+ [connection, query, copiedParams]() -> std::shared_ptr<HybridNitroSQLiteQueryResultSpec> {
144
+ auto result = sqliteExecute(connection, query, copiedParams);
108
145
  return result;
109
146
  });
110
147
  };
@@ -122,9 +159,15 @@ std::shared_ptr<Promise<BatchQueryResult>> HybridNitroSQLite::executeBatchAsync(
122
159
  // ArrayBuffers into native buffers before going off-thread.
123
160
  const auto commands = batchParamsToCommands(batchParams);
124
161
  const auto copiedCommands = copyArrayBufferParamsForBackground(commands);
162
+ SQLiteConnectionPtr connection;
163
+ try {
164
+ connection = sqliteGetOpenDatabase(dbName);
165
+ } catch (...) {
166
+ return Promise<BatchQueryResult>::rejected(std::current_exception());
167
+ }
125
168
 
126
- return Promise<BatchQueryResult>::async([=, this]() -> BatchQueryResult {
127
- auto result = sqliteExecuteBatch(dbName, copiedCommands);
169
+ return Promise<BatchQueryResult>::async([connection, copiedCommands]() -> BatchQueryResult {
170
+ auto result = sqliteExecuteBatch(connection, copiedCommands);
128
171
  return BatchQueryResult(result.rowsAffected);
129
172
  });
130
173
  };
@@ -135,9 +178,15 @@ FileLoadResult HybridNitroSQLite::loadFile(const std::string& dbName, const std:
135
178
  };
136
179
 
137
180
  std::shared_ptr<Promise<FileLoadResult>> HybridNitroSQLite::loadFileAsync(const std::string& dbName, const std::string& location) {
138
- return Promise<FileLoadResult>::async([=, this]() -> FileLoadResult {
139
- auto result = loadFile(dbName, location);
140
- return result;
181
+ SQLiteConnectionPtr connection;
182
+ try {
183
+ connection = sqliteGetOpenDatabase(dbName);
184
+ } catch (...) {
185
+ return Promise<FileLoadResult>::rejected(std::current_exception());
186
+ }
187
+ return Promise<FileLoadResult>::async([connection, location]() -> FileLoadResult {
188
+ const auto result = importSqlFile(connection, location);
189
+ return FileLoadResult(result.commands, result.rowsAffected);
141
190
  });
142
191
  };
143
192
 
@@ -14,6 +14,10 @@ public:
14
14
 
15
15
  public:
16
16
  static std::string docPath;
17
+ // Directory databases were stored in by previous app versions, when the platform layer has
18
+ // relocated docPath (e.g. iOS with RNNitroSQLite_DatabaseLocation set to "ApplicationSupport").
19
+ // When non-empty, databases found there are resolved as they are opened, attached, or dropped.
20
+ static std::string migrationDocPath;
17
21
 
18
22
  public:
19
23
  // Methods
@@ -43,5 +47,6 @@ public:
43
47
  };
44
48
 
45
49
  inline std::string HybridNitroSQLite::docPath = "";
50
+ inline std::string HybridNitroSQLite::migrationDocPath = "";
46
51
 
47
52
  } // namespace margelo::nitro::rnnitrosqlite
@@ -10,6 +10,8 @@ namespace margelo::nitro::rnnitrosqlite {
10
10
 
11
11
  namespace {
12
12
 
13
+ constexpr size_t nodePadding = 24;
14
+
13
15
  /**
14
16
  * Compute the approximate external memory size of a single result row.
15
17
  * This includes:
@@ -18,8 +20,7 @@ namespace {
18
20
  */
19
21
  size_t getRowExternalMemorySize(const SQLiteQueryResultRow& row) {
20
22
  size_t bucketMemory = row.bucket_count() * sizeof(void*);
21
- constexpr size_t nodePadding = 24;
22
- size_t nodesMemory = row.size() * (sizeof(std::pair<std::string, SQLiteValue>) * nodePadding);
23
+ size_t nodesMemory = row.size() * (sizeof(std::pair<std::string, SQLiteValue>) + nodePadding);
23
24
  return bucketMemory + nodesMemory;
24
25
  }
25
26
 
@@ -49,10 +50,10 @@ namespace {
49
50
  * - Metadata contents, especially the `name` string on each metadata entry.
50
51
  */
51
52
  size_t getMetadataExternalMemorySize(const SQLiteQueryTableMetadata& metadata) {
52
- size_t size = 0;
53
+ size_t size = metadata.bucket_count() * sizeof(void*);
54
+ size += metadata.size() * (sizeof(SQLiteQueryTableMetadata::value_type) + nodePadding);
53
55
 
54
56
  for (const auto& [columnName, columnMeta] : metadata) {
55
-
56
57
  size += columnName.capacity();
57
58
  size += columnMeta.name.capacity();
58
59
  }
@@ -13,7 +13,7 @@ public:
13
13
  HybridNitroSQLiteQueryResult() : HybridObject(TAG) {}
14
14
  HybridNitroSQLiteQueryResult(SQLiteQueryResults results, std::optional<double> insertId, double rowsAffected,
15
15
  std::optional<SQLiteQueryTableMetadata> metadata)
16
- : HybridObject(TAG), _insertId(insertId), _rowsAffected(rowsAffected), _results(std::move(results)), _metadata(metadata) {}
16
+ : HybridObject(TAG), _insertId(insertId), _rowsAffected(rowsAffected), _results(std::move(results)), _metadata(std::move(metadata)) {}
17
17
 
18
18
  private:
19
19
  std::optional<double> _insertId;
@@ -11,21 +11,26 @@
11
11
  namespace margelo::rnnitrosqlite {
12
12
 
13
13
  SQLiteOperationResult importSqlFile(const std::string& dbName, const std::string& fileLocation) {
14
+ return importSqlFile(sqliteGetOpenDatabase(dbName), fileLocation);
15
+ }
16
+
17
+ SQLiteOperationResult importSqlFile(const SQLiteConnectionPtr& connection, const std::string& fileLocation) {
18
+ std::lock_guard lock(connection->mutex);
14
19
  std::string line;
15
20
  std::ifstream sqFile(fileLocation);
16
21
  if (sqFile.is_open()) {
17
22
  try {
18
23
  int rowsAffected = 0;
19
24
  int commands = 0;
20
- sqliteExecuteLiteral(dbName, "BEGIN EXCLUSIVE TRANSACTION");
25
+ sqliteExecuteCommand(connection, "BEGIN EXCLUSIVE TRANSACTION");
21
26
  while (std::getline(sqFile, line, '\n')) {
22
27
  if (!line.empty()) {
23
28
  try {
24
- SQLiteOperationResult result = sqliteExecuteLiteral(dbName, line);
29
+ SQLiteOperationResult result = sqliteExecuteCommand(connection, line);
25
30
  rowsAffected += result.rowsAffected;
26
31
  commands++;
27
32
  } catch (NitroSQLiteException& e) {
28
- sqliteExecuteLiteral(dbName, "ROLLBACK");
33
+ sqliteExecuteCommand(connection, "ROLLBACK");
29
34
  sqFile.close();
30
35
  throw NitroSQLiteException::CouldNotLoadFile(fileLocation, "Transaction was rolled back");
31
36
  }
@@ -33,11 +38,11 @@ SQLiteOperationResult importSqlFile(const std::string& dbName, const std::string
33
38
  }
34
39
 
35
40
  sqFile.close();
36
- sqliteExecuteLiteral(dbName, "COMMIT");
41
+ sqliteExecuteCommand(connection, "COMMIT");
37
42
  return {.rowsAffected = rowsAffected, .commands = commands};
38
43
  } catch (...) {
39
44
  sqFile.close();
40
- sqliteExecuteLiteral(dbName, "ROLLBACK");
45
+ sqliteExecuteCommand(connection, "ROLLBACK");
41
46
  throw NitroSQLiteException(NitroSQLiteExceptionType::UnknownError, "Unexpected error. Transaction was rolled back");
42
47
  }
43
48
  } else {
@@ -7,9 +7,13 @@
7
7
  #pragma once
8
8
 
9
9
  #include "types.hpp"
10
+ #include <memory>
10
11
 
11
12
  namespace margelo::rnnitrosqlite {
12
13
 
14
+ struct SQLiteConnection;
15
+
13
16
  SQLiteOperationResult importSqlFile(const std::string& dbName, const std::string& fileLocation);
17
+ SQLiteOperationResult importSqlFile(const std::shared_ptr<SQLiteConnection>& connection, const std::string& fileLocation);
14
18
 
15
- }
19
+ } // namespace margelo::rnnitrosqlite