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.
- package/RNNitroSQLite.podspec +56 -20
- package/android/CMakeLists.txt +15 -0
- package/android/build.gradle +14 -0
- package/cpp/NitroSQLiteException.hpp +5 -0
- package/cpp/databaseMigration.cpp +123 -0
- package/cpp/databaseMigration.hpp +13 -0
- package/cpp/hybridObjects/HybridNitroSQLite.cpp +65 -16
- package/cpp/hybridObjects/HybridNitroSQLite.hpp +5 -0
- package/cpp/hybridObjects/HybridNitroSQLiteQueryResult.cpp +5 -4
- package/cpp/hybridObjects/HybridNitroSQLiteQueryResult.hpp +1 -1
- package/cpp/importSqlFile.cpp +10 -5
- package/cpp/importSqlFile.hpp +5 -1
- package/cpp/operations.cpp +232 -168
- package/cpp/operations.hpp +31 -1
- package/cpp/sqliteExecuteBatch.cpp +17 -16
- package/cpp/sqliteExecuteBatch.hpp +4 -0
- package/ios/OnLoad.mm +26 -3
- package/lib/commonjs/DatabaseQueue.js +4 -17
- package/lib/commonjs/DatabaseQueue.js.map +1 -1
- package/lib/commonjs/operations/execute.js +28 -11
- package/lib/commonjs/operations/execute.js.map +1 -1
- package/lib/commonjs/operations/session.js +34 -9
- package/lib/commonjs/operations/session.js.map +1 -1
- package/lib/commonjs/operations/transaction.js +5 -5
- package/lib/commonjs/operations/transaction.js.map +1 -1
- package/lib/module/DatabaseQueue.js +4 -15
- package/lib/module/DatabaseQueue.js.map +1 -1
- package/lib/module/operations/execute.js +24 -12
- package/lib/module/operations/execute.js.map +1 -1
- package/lib/module/operations/session.js +36 -11
- package/lib/module/operations/session.js.map +1 -1
- package/lib/module/operations/transaction.js +6 -6
- package/lib/module/operations/transaction.js.map +1 -1
- package/lib/typescript/commonjs/DatabaseQueue.d.ts +1 -3
- package/lib/typescript/commonjs/DatabaseQueue.d.ts.map +1 -1
- package/lib/typescript/commonjs/operations/execute.d.ts +6 -0
- package/lib/typescript/commonjs/operations/execute.d.ts.map +1 -1
- package/lib/typescript/commonjs/operations/session.d.ts.map +1 -1
- package/lib/typescript/commonjs/specs/NitroSQLite.nitro.d.ts.map +1 -1
- package/lib/typescript/commonjs/specs/NitroSQLiteQueryResult.nitro.d.ts.map +1 -1
- package/lib/typescript/module/DatabaseQueue.d.ts +1 -3
- package/lib/typescript/module/DatabaseQueue.d.ts.map +1 -1
- package/lib/typescript/module/operations/execute.d.ts +6 -0
- package/lib/typescript/module/operations/execute.d.ts.map +1 -1
- package/lib/typescript/module/operations/session.d.ts.map +1 -1
- package/lib/typescript/module/specs/NitroSQLite.nitro.d.ts.map +1 -1
- package/lib/typescript/module/specs/NitroSQLiteQueryResult.nitro.d.ts.map +1 -1
- package/nitrogen/generated/android/RNNitroSQLiteOnLoad.cpp +2 -2
- package/nitrogen/generated/android/c++/JHybridNitroSQLiteOnLoadSpec.hpp +2 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/rnnitrosqlite/HybridNitroSQLiteOnLoadSpec.kt +2 -0
- package/nitrogen/generated/ios/RNNitroSQLite+autolinking.rb +2 -0
- package/package.json +9 -7
- package/src/DatabaseQueue.ts +9 -19
- package/src/operations/execute.ts +52 -12
- package/src/operations/session.ts +57 -11
- package/src/operations/transaction.ts +6 -6
- package/src/specs/NitroSQLite.nitro.ts +4 -1
- package/src/specs/NitroSQLiteQueryResult.nitro.ts +4 -1
- package/android/.gradle/8.9/checksums/checksums.lock +0 -0
- package/android/.gradle/8.9/checksums/md5-checksums.bin +0 -0
- package/android/.gradle/8.9/checksums/sha1-checksums.bin +0 -0
- package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
- package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
- package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/8.9/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
- package/android/.gradle/vcs-1/gc.properties +0 -0
package/RNNitroSQLite.podspec
CHANGED
|
@@ -1,11 +1,48 @@
|
|
|
1
1
|
require "json"
|
|
2
2
|
|
|
3
3
|
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
4
|
-
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
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"
|
package/android/CMakeLists.txt
CHANGED
|
@@ -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}
|
package/android/build.gradle
CHANGED
|
@@ -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 =
|
|
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
|
|
79
|
-
|
|
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
|
-
|
|
85
|
-
|
|
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
|
-
[
|
|
107
|
-
auto result = sqliteExecute(
|
|
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([
|
|
127
|
-
auto result = sqliteExecuteBatch(
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
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 =
|
|
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;
|
package/cpp/importSqlFile.cpp
CHANGED
|
@@ -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
|
-
|
|
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 =
|
|
29
|
+
SQLiteOperationResult result = sqliteExecuteCommand(connection, line);
|
|
25
30
|
rowsAffected += result.rowsAffected;
|
|
26
31
|
commands++;
|
|
27
32
|
} catch (NitroSQLiteException& e) {
|
|
28
|
-
|
|
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
|
-
|
|
41
|
+
sqliteExecuteCommand(connection, "COMMIT");
|
|
37
42
|
return {.rowsAffected = rowsAffected, .commands = commands};
|
|
38
43
|
} catch (...) {
|
|
39
44
|
sqFile.close();
|
|
40
|
-
|
|
45
|
+
sqliteExecuteCommand(connection, "ROLLBACK");
|
|
41
46
|
throw NitroSQLiteException(NitroSQLiteExceptionType::UnknownError, "Unexpected error. Transaction was rolled back");
|
|
42
47
|
}
|
|
43
48
|
} else {
|
package/cpp/importSqlFile.hpp
CHANGED
|
@@ -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
|