diff --git a/src/bridge/cppbridge/CoreBridge.cpp b/src/bridge/cppbridge/CoreBridge.cpp index 89d038df7..e4760af33 100644 --- a/src/bridge/cppbridge/CoreBridge.cpp +++ b/src/bridge/cppbridge/CoreBridge.cpp @@ -41,3 +41,8 @@ void WCDBCorePurgeAllDatabase(void) { WCDB::Core::shared().purgeDatabasePool(); } + +bool WCDBCoreSetDefaultTemporaryDirectory(const char* _Nullable dir) +{ + return WCDB::Core::shared().setDefaultTemporaryDirectory(dir); +} diff --git a/src/bridge/cppbridge/CoreBridge.h b/src/bridge/cppbridge/CoreBridge.h index 7721861bc..5072b0d6c 100644 --- a/src/bridge/cppbridge/CoreBridge.h +++ b/src/bridge/cppbridge/CoreBridge.h @@ -31,5 +31,6 @@ WCDB_EXTERN_C_BEGIN CPPDatabase WCDBCoreCreateDatabase(const char* _Nonnull path); void WCDBCoreSetDefaultCipherConfig(int version); void WCDBCorePurgeAllDatabase(void); +bool WCDBCoreSetDefaultTemporaryDirectory(const char* _Nullable dir); WCDB_EXTERN_C_END diff --git a/src/common/base/FileManager.cpp b/src/common/base/FileManager.cpp index afba375fd..895a1bcbb 100644 --- a/src/common/base/FileManager.cpp +++ b/src/common/base/FileManager.cpp @@ -463,6 +463,12 @@ bool FileManager::moveItems(const std::list> & bool FileManager::createDirectoryWithIntermediateDirectories(const UnsafeStringView &directory) { + if (directory.length() == 0) { + Error error(Error::Code::IOError, Error::Level::Error, "empty directory"); + Notifier::shared().notify(error); + SharedThreadedErrorProne::setThreadedError(std::move(error)); + return false; + } auto exists = directoryExists(directory); if (!exists.succeed()) { return false; diff --git a/src/common/base/Path.cpp b/src/common/base/Path.cpp index df8a59402..3aa47df1f 100644 --- a/src/common/base/Path.cpp +++ b/src/common/base/Path.cpp @@ -66,7 +66,11 @@ StringView getDirectoryName(const UnsafeStringView &base) { std::string dir = base.data(); int64_t found = dir.find_last_of(kPathSeparator); - return StringView(dir.substr(0, found)); + if (found >= 0) { + return StringView(dir.substr(0, found)); + } else { + return StringView(); + } } StringView normalize(const UnsafeStringView &path) diff --git a/src/common/base/SQLite.h b/src/common/base/SQLite.h index 6cee59dab..0826cd9ce 100644 --- a/src/common/base/SQLite.h +++ b/src/common/base/SQLite.h @@ -32,6 +32,8 @@ extern "C" { #endif +extern char *sqlite3_temp_directory; + int sqlcipher_set_default_hmac_algorithm(int algorithm); int sqlcipher_set_default_kdf_algorithm(int algorithm); void sqlcipher_set_default_kdf_iter(int iter); diff --git a/src/common/base/StringView.cpp b/src/common/base/StringView.cpp index 739db2b50..e52ba6f87 100644 --- a/src/common/base/StringView.cpp +++ b/src/common/base/StringView.cpp @@ -407,6 +407,25 @@ StringView StringView::makeConstant(const char* string) return ret; } +StringView StringView::createConstant(const char* string) +{ + StringView ret; + if (string != nullptr) { + size_t length = strlen(string); + char* data = (char*) malloc((length + 1) * sizeof(char)); + if (data != nullptr) { + memcpy((void*) data, (void*) string, length); + data[length] = '\0'; + } else { + length = 0; + } + ret.m_data = data; + ret.m_length = length; + ret.m_referenceCount = (std::atomic*) ConstanceReference; + } + return ret; +} + bool StringViewComparator::operator()(const StringView& lhs, const StringView& rhs) const { return lhs.compare(rhs) < 0; diff --git a/src/common/base/StringView.hpp b/src/common/base/StringView.hpp index 8fb6ce31b..4bb4a3428 100644 --- a/src/common/base/StringView.hpp +++ b/src/common/base/StringView.hpp @@ -129,6 +129,7 @@ class StringView final : public UnsafeStringView { static StringView formatted(const char* format, ...); static StringView hexString(const UnsafeData& data); static StringView makeConstant(const char* string); + static StringView createConstant(const char* string); protected: void assignString(const char* content, size_t length); diff --git a/src/common/core/Core.cpp b/src/common/core/Core.cpp index 2db646cd9..97dfab54a 100644 --- a/src/common/core/Core.cpp +++ b/src/common/core/Core.cpp @@ -404,19 +404,19 @@ void Core::setNotificationWhenErrorTraced(const UnsafeStringView& path, } #pragma mark - Config -void Core::setABTestConfig(const UnsafeStringView configName, const UnsafeStringView configValue) +void Core::setABTestConfig(const UnsafeStringView& configName, const UnsafeStringView& configValue) { LockGuard memoryGuard(m_memory); m_abtestConfig[configName] = configValue; } -void Core::removeABTestConfig(const UnsafeStringView configName) +void Core::removeABTestConfig(const UnsafeStringView& configName) { LockGuard memoryGuard(m_memory); m_abtestConfig.erase(configName); } -Optional Core::getABTestConfig(UnsafeStringView configName) +Optional Core::getABTestConfig(const UnsafeStringView& configName) { SharedLockGuard memoryGuard(m_memory); if (m_abtestConfig.find(configName) != m_abtestConfig.end()) { @@ -458,4 +458,15 @@ void Core::setDefaultCipherConfiguration(int version) } } +bool Core::setDefaultTemporaryDirectory(const UnsafeStringView& dir) +{ + if (dir.length() > 0) { + if (!FileManager::createDirectoryWithIntermediateDirectories(dir)) { + return false; + } + } + sqlite3_temp_directory = (char*) StringView::createConstant(dir.data()).data(); + return true; +} + } // namespace WCDB diff --git a/src/common/core/Core.hpp b/src/common/core/Core.hpp index 7fe662dc3..3ed2152d1 100644 --- a/src/common/core/Core.hpp +++ b/src/common/core/Core.hpp @@ -153,11 +153,13 @@ class Core final : public DatabasePoolEvent, public OperationEvent { #pragma mark - Config public: - void setABTestConfig(const UnsafeStringView configName, const UnsafeStringView configValue); - void removeABTestConfig(const UnsafeStringView configName); - Optional getABTestConfig(UnsafeStringView configName); + void setABTestConfig(const UnsafeStringView& configName, + const UnsafeStringView& configValue); + void removeABTestConfig(const UnsafeStringView& configName); + Optional getABTestConfig(const UnsafeStringView& configName); void setDefaultCipherConfiguration(int version); + bool setDefaultTemporaryDirectory(const UnsafeStringView& dir); protected: Configs m_configs; diff --git a/src/cpp/core/Database.cpp b/src/cpp/core/Database.cpp index b634ef44b..2397a7a8b 100644 --- a/src/cpp/core/Database.cpp +++ b/src/cpp/core/Database.cpp @@ -424,9 +424,9 @@ void Database::removeConfig(const UnsafeStringView& name) m_innerDatabase->removeConfig(name); } -void Database::setDefaultTemporaryDirectory(UnsafeStringView dir) +bool Database::setDefaultTemporaryDirectory(const UnsafeStringView& directory) { - setenv("SQLITE_TMPDIR", dir.data(), 1); + return Core::shared().setDefaultTemporaryDirectory(directory); } void Database::filterMigration(MigrationFilter filter) diff --git a/src/cpp/core/Database.hpp b/src/cpp/core/Database.hpp index ac3bf7da9..29852a177 100644 --- a/src/cpp/core/Database.hpp +++ b/src/cpp/core/Database.hpp @@ -592,8 +592,10 @@ class Database final : public HandleORMOperation { 4. /tmp; 5. The current working directory (".") Please see: https://www.sqlite.org/tempfiles.html + @param directory a global temporary directory. + @return true if directory exists or create directory success. */ - static void setDefaultTemporaryDirectory(UnsafeStringView dir); + static bool setDefaultTemporaryDirectory(const UnsafeStringView &directory); #pragma mark - Migration typedef struct MigrationInfo { diff --git a/src/cpp/tests/interface/CPPConfigTests.mm b/src/cpp/tests/interface/CPPConfigTests.mm index 861c1bec6..3829ce907 100644 --- a/src/cpp/tests/interface/CPPConfigTests.mm +++ b/src/cpp/tests/interface/CPPConfigTests.mm @@ -191,4 +191,19 @@ - (void)test_cipher_with_diferent_version TestCaseAssertTrue(self.database->canOpen()); } +- (void)testSetTemporaryDirectory +{ + TestCaseAssertFalse(WCDB::Database::setDefaultTemporaryDirectory("wrongDir")); + + NSString* tempDir = [NSTemporaryDirectory() stringByAppendingPathComponent:@"dbTempDir"]; + TestCaseAssertTrue(WCDB::Database::setDefaultTemporaryDirectory(tempDir.UTF8String)); + WCDB::StatementPragma getDirStatement = WCDB::StatementPragma().pragma(WCDB::Pragma("temp_store_directory")); + WCDB::OptionalValue dir = self.database->getValueFromStatement(getDirStatement); + TestCaseAssertTrue(dir.hasValue() && dir.value().textValue().compare(tempDir.UTF8String) == 0); + + TestCaseAssertTrue(WCDB::Database::setDefaultTemporaryDirectory(NULL)); + dir = self.database->getValueFromStatement(getDirStatement); + TestCaseAssertTrue(dir.hasValue() && dir.value().textValue().length() == 0); +} + @end diff --git a/src/objc/database/WCTDatabase+File.h b/src/objc/database/WCTDatabase+File.h index b7aa10b55..f4d58deef 100644 --- a/src/objc/database/WCTDatabase+File.h +++ b/src/objc/database/WCTDatabase+File.h @@ -63,8 +63,10 @@ NS_ASSUME_NONNULL_BEGIN 4. /tmp; 5. The current working directory (".") Please see: https://www.sqlite.org/tempfiles.html + @param directory a global temporary directory. + @return YES if directory exists or create directory success. */ -+ (BOOL)setDefaultTemporaryDatabaseFileDirectory:(NSString *)dir; ++ (BOOL)setDefaultTemporaryDatabaseFileDirectory:(NSString *_Nullable)directory; @end diff --git a/src/objc/database/WCTDatabase+File.mm b/src/objc/database/WCTDatabase+File.mm index da8732ad7..94e33f074 100644 --- a/src/objc/database/WCTDatabase+File.mm +++ b/src/objc/database/WCTDatabase+File.mm @@ -53,43 +53,9 @@ - (WCTOptionalSize)getFilesSize return _database->getFilesSize(); } -+ (BOOL)setDefaultTemporaryDatabaseFileDirectory:(NSString *)dir ++ (BOOL)setDefaultTemporaryDatabaseFileDirectory:(NSString *_Nullable)directory { - if (dir.length == 0) { - WCDB::Error error(WCDB::Error::Code::Error, - WCDB::Error::Level::Error, - "Set nil temporary database files directory!"); - WCDB::Notifier::shared().notify(error); - return NO; - } - NSFileManager *fileManager = [NSFileManager defaultManager]; - BOOL isDir = FALSE; - BOOL isDirExist = [fileManager fileExistsAtPath:dir - isDirectory:&isDir]; - if (!isDirExist) { - BOOL bCreateDir = [fileManager createDirectoryAtPath:dir - withIntermediateDirectories:YES - attributes:nil - error:nil]; - if (!bCreateDir) { - WCDB::Error error(WCDB::Error::Code::Error, - WCDB::Error::Level::Error, - "Fail to create temporary database files directory"); - error.infos.insert_or_assign("Path", dir.UTF8String); - WCDB::Notifier::shared().notify(error); - return NO; - } - } else if (!isDir) { - WCDB::Error error(WCDB::Error::Code::Error, - WCDB::Error::Level::Error, - "Invalid directory"); - error.infos.insert_or_assign("Path", dir.UTF8String); - WCDB::Notifier::shared().notify(error); - return NO; - } - - setenv("SQLITE_TMPDIR", dir.UTF8String, 1); - return YES; + return WCDB::Core::shared().setDefaultTemporaryDirectory(directory); } @end diff --git a/src/objc/tests/config/ConfigTests.mm b/src/objc/tests/config/ConfigTests.mm index f50cffd7e..ffef4fe88 100644 --- a/src/objc/tests/config/ConfigTests.mm +++ b/src/objc/tests/config/ConfigTests.mm @@ -237,4 +237,21 @@ - (void)test_abtest_config }]; } +- (void)testSetTemporaryDirectory +{ + NSString* wrongDir = @"wrongDir"; + TestCaseAssertFalse([WCTDatabase setDefaultTemporaryDatabaseFileDirectory:wrongDir]); + + NSString* tempDir = [NSTemporaryDirectory() stringByAppendingPathComponent:@"dbTempDir"]; + TestCaseAssertTrue([WCTDatabase setDefaultTemporaryDatabaseFileDirectory:tempDir]); + + WCDB::StatementPragma getDirStatement = WCDB::StatementPragma().pragma(WCDB::Pragma("temp_store_directory")); + WCTValue* dir = [self.database getValueFromStatement:getDirStatement]; + TestCaseAssertTrue(dir != nil && [dir.stringValue isEqualToString:tempDir]); + + TestCaseAssertTrue([WCTDatabase setDefaultTemporaryDatabaseFileDirectory:nil]); + dir = [self.database getValueFromStatement:getDirStatement]; + TestCaseAssertTrue(dir != nil && [dir.stringValue isEqualToString:@""]); +} + @end diff --git a/src/swift/core/base/Database.swift b/src/swift/core/base/Database.swift index 2e17c623a..5ee08b0f0 100644 --- a/src/swift/core/base/Database.swift +++ b/src/swift/core/base/Database.swift @@ -280,8 +280,10 @@ public extension Database { /// 4. /tmp; /// 5. The current working directory (".") /// Please see: https://www.sqlite.org/tempfiles.html - static func setDefaultTemporaryDirectory(_ dir: String) { - setenv("SQLITE_TMPDIR".cString, dir.cString, 1) + /// - Parameter directory a global temporary directory. + /// - Returns: true if directory exists or create directory success. + static func setDefaultTemporaryDirectory(_ directory: String) -> Bool { + return WCDBCoreSetDefaultTemporaryDirectory(directory.cString) } typealias PerformanceTracer = (String, UInt64, String, Double) -> Void // Path, handleIdentifier, SQL, cost diff --git a/src/swift/tests/crud/AdvanceTests.swift b/src/swift/tests/crud/AdvanceTests.swift index cf7cc26e0..cf8dff4c8 100644 --- a/src/swift/tests/crud/AdvanceTests.swift +++ b/src/swift/tests/crud/AdvanceTests.swift @@ -922,4 +922,19 @@ class AdvanceTests: CRUDTestCase { XCTAssertEqual(migratedTable ?? "", "sourceTable") } + + func testDefaultTemporaryDirectory() { + XCTAssertFalse(Database.setDefaultTemporaryDirectory("wrongDir")) + + let tempDir = NSTemporaryDirectory().appending("/dbTempDir") + XCTAssertTrue(Database.setDefaultTemporaryDirectory(tempDir)) + + let getDirStatement = StatementPragma().pragma(Pragma(named: "temp_store_directory")) + var dir = WCDBAssertNoThrowReturned(try database.getValue(from: getDirStatement)) + XCTAssertTrue(dir != nil && dir!.stringValue == tempDir) + + XCTAssertTrue(Database.setDefaultTemporaryDirectory("")) + dir = WCDBAssertNoThrowReturned(try database.getValue(from: getDirStatement)) + XCTAssertTrue(dir != nil && dir!.stringValue == "") + } }