77#include " ../NitroSQLiteMacros.hpp"
88#include " ../NitroSQLiteOperations.hpp"
99#include " HybridPreparedStatement.hpp"
10+ #include < cstdlib>
1011#include < exception>
1112#include < filesystem>
1213#include < iostream>
1314#include < map>
15+ #include < mutex>
1416#include < optional>
17+ #include < stdexcept>
1518#include < string>
1619#include < utility>
1720#include < variant>
1821#include < vector>
1922
2023namespace margelo ::nitro::rnnitrosqlite {
2124
25+ // SQLite caches SQLITE_TMPDIR when it initializes its VFS, so only the first root sets it.
26+ static void configureTemporaryDirectory (const std::string& temporaryDirectory) {
27+ static std::once_flag configureOnce;
28+ static bool configured = false ;
29+ std::call_once (configureOnce, [&] { configured = setenv (" SQLITE_TMPDIR" , temporaryDirectory.c_str (), 1 ) == 0 ; });
30+ if (!configured) {
31+ throw std::runtime_error (" Could not configure SQLite temporary directory" );
32+ }
33+ }
34+
35+ HybridNitroSQLite::HybridNitroSQLite (std::string databaseDirectory, const std::optional<std::string>& temporaryDirectory,
36+ const std::optional<std::string>& legacyDatabaseDirectory)
37+ : _databaseDirectory(std::move(databaseDirectory)), _legacyDatabaseDirectory(legacyDatabaseDirectory.value_or(" " )) {
38+ if (temporaryDirectory) {
39+ configureTemporaryDirectory (*temporaryDirectory);
40+ }
41+ }
42+
2243// Copy any JS-backed ArrayBuffers on the JS thread so they can be safely
2344// accessed from the connection's background worker.
2445static std::optional<SQLiteQueryParams> copyArrayBufferParamsForBackground (const std::optional<SQLiteQueryParams>& params) {
@@ -90,34 +111,35 @@ static Promise<Result> enqueueConnectionOperation(const SQLiteConnectionPtr& con
90111 return promise;
91112}
92113
93- const std::string getDocPath (const std::optional<std::string>& location) {
114+ const std::string getDocPath (const std::string& databaseDirectory, const std:: optional<std::string>& location) {
94115 if (location && location->find (' \0 ' ) != std::string::npos) {
95116 throw NitroSQLiteException (NitroSQLiteExceptionType::DatabaseCannotBeOpened, " Database location contains a NUL byte" );
96117 }
97- std::string tempDocPath = std::string (HybridNitroSQLite::docPath) ;
118+ std::string tempDocPath = databaseDirectory ;
98119 if (location) {
99120 tempDocPath = tempDocPath + " /" + *location;
100121 }
101122
102123 return tempDocPath;
103124}
104125
105- const std::string getOldDocPath (const std::optional<std::string>& location) {
106- std::string oldDocPath = HybridNitroSQLite::migrationDocPath ;
126+ const std::string getOldDocPath (const std::string& legacyDatabaseDirectory, const std:: optional<std::string>& location) {
127+ std::string oldDocPath = legacyDatabaseDirectory ;
107128 if (location) {
108129 oldDocPath = oldDocPath + " /" + *location;
109130 }
110131
111132 return oldDocPath;
112133}
113134
114- const std::string getMigratedDocPath (DatabaseConnections& connections, const std::string& dbName,
135+ const std::string getMigratedDocPath (DatabaseConnections& connections, const std::string& databaseDirectory,
136+ const std::string& legacyDatabaseDirectory, const std::string& dbName,
115137 const std::optional<std::string>& location, bool readOnly = false ) {
116- const auto currentDocPath = getDocPath (location);
117- if (HybridNitroSQLite::migrationDocPath .empty ()) {
138+ const auto currentDocPath = getDocPath (databaseDirectory, location);
139+ if (legacyDatabaseDirectory .empty ()) {
118140 return currentDocPath;
119141 }
120- const auto oldDocPath = getOldDocPath (location);
142+ const auto oldDocPath = getOldDocPath (legacyDatabaseDirectory, location);
121143 std::string selectedPath;
122144 connections.withConnectionsLocked ([&]() {
123145 const auto oldPath = std::filesystem::path (oldDocPath) / dbName;
@@ -140,7 +162,8 @@ void HybridNitroSQLite::open(const std::string& dbName, const std::optional<std:
140162 if (_connections.isOpen (dbName)) {
141163 throw NitroSQLiteException::DatabaseAlreadyOpen (dbName);
142164 }
143- const auto docPath = getMigratedDocPath (_connections, dbName, location, readOnly.value_or (false ));
165+ const auto docPath =
166+ getMigratedDocPath (_connections, _databaseDirectory, _legacyDatabaseDirectory, dbName, location, readOnly.value_or (false ));
144167 sqliteOpenDb (_connections, dbName, docPath, readOnly.value_or (false ), encryptionKey);
145168}
146169
@@ -152,7 +175,8 @@ std::string HybridNitroSQLite::openConnection(const std::string& dbName, const s
152175 " Independent connections require a thread-safe SQLite build" );
153176 }
154177 std::lock_guard lock (_connections.lifecycleMutex );
155- const auto docPath = getMigratedDocPath (_connections, dbName, location, readOnly.value_or (false ));
178+ const auto docPath =
179+ getMigratedDocPath (_connections, _databaseDirectory, _legacyDatabaseDirectory, dbName, location, readOnly.value_or (false ));
156180 return sqliteOpenConnection (_connections, dbName, docPath, readOnly.value_or (false ), encryptionKey);
157181}
158182
@@ -168,13 +192,13 @@ void HybridNitroSQLite::drop(const std::string& dbName, const std::optional<std:
168192 const std::optional<std::string>& connectionId) {
169193 validateDatabaseName (dbName);
170194 std::lock_guard lock (_connections.lifecycleMutex );
171- const auto currentDocPath = getDocPath (location);
172- if (migrationDocPath .empty ()) {
195+ const auto currentDocPath = getDocPath (_databaseDirectory, location);
196+ if (_legacyDatabaseDirectory .empty ()) {
173197 sqliteRemoveDb (_connections, dbName, currentDocPath, connectionId);
174198 return ;
175199 }
176200
177- const auto oldDocPath = getOldDocPath (location);
201+ const auto oldDocPath = getOldDocPath (_legacyDatabaseDirectory, location);
178202 const auto preferredPath = _connections.physicalPathForKey (connectionId.value_or (dbName));
179203 const auto currentPath = std::filesystem::path (currentDocPath) / dbName;
180204 std::error_code ec;
@@ -199,7 +223,7 @@ void HybridNitroSQLite::attach(const std::string& mainDbName, const std::string&
199223 if (_connections.get (mainDbName)->readOnly ) {
200224 throw NitroSQLiteException (NitroSQLiteExceptionType::UnableToAttachToDatabase, " Cannot attach a database to a read-only connection" );
201225 }
202- const auto attachedDocPath = getMigratedDocPath (_connections, dbNameToAttach, location);
226+ const auto attachedDocPath = getMigratedDocPath (_connections, _databaseDirectory, _legacyDatabaseDirectory, dbNameToAttach, location);
203227 sqliteAttachDb (_connections.get (mainDbName), attachedDocPath, dbNameToAttach, alias);
204228};
205229
0 commit comments