| 199 | } |
| 200 | |
| 201 | void SQLiteDatabase::Open() |
| 202 | { |
| 203 | int flags = SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; |
| 204 | if (m_mock) { |
| 205 | flags |= SQLITE_OPEN_MEMORY; // In memory database for mock db |
| 206 | } |
| 207 | |
| 208 | if (m_db == nullptr) { |
| 209 | if (!m_mock) { |
| 210 | TryCreateDirectories(fs::PathFromString(m_dir_path)); |
| 211 | } |
| 212 | int ret = sqlite3_open_v2(m_file_path.c_str(), &m_db, flags, nullptr); |
| 213 | if (ret != SQLITE_OK) { |
| 214 | throw std::runtime_error(strprintf("SQLiteDatabase: Failed to open database: %s\n", sqlite3_errstr(ret))); |
| 215 | } |
| 216 | ret = sqlite3_extended_result_codes(m_db, 1); |
| 217 | if (ret != SQLITE_OK) { |
| 218 | throw std::runtime_error(strprintf("SQLiteDatabase: Failed to enable extended result codes: %s\n", sqlite3_errstr(ret))); |
| 219 | } |
| 220 | } |
| 221 | |
| 222 | if (sqlite3_db_readonly(m_db, "main") != 0) { |
| 223 | throw std::runtime_error("SQLiteDatabase: Database opened in readonly mode but read-write permissions are needed"); |
| 224 | } |
| 225 | |
| 226 | // Acquire an exclusive lock on the database |
| 227 | // First change the locking mode to exclusive |
| 228 | SetPragma(m_db, "locking_mode", "exclusive", "Unable to change database locking mode to exclusive"); |
| 229 | // Now begin a transaction to acquire the exclusive lock. This lock won't be released until we close because of the exclusive locking mode. |
| 230 | int ret = sqlite3_exec(m_db, "BEGIN EXCLUSIVE TRANSACTION", nullptr, nullptr, nullptr); |
| 231 | if (ret != SQLITE_OK) { |
| 232 | throw std::runtime_error("SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another instance of " PACKAGE_NAME "?\n"); |
| 233 | } |
| 234 | ret = sqlite3_exec(m_db, "COMMIT", nullptr, nullptr, nullptr); |
| 235 | if (ret != SQLITE_OK) { |
| 236 | throw std::runtime_error(strprintf("SQLiteDatabase: Unable to end exclusive lock transaction: %s\n", sqlite3_errstr(ret))); |
| 237 | } |
| 238 | |
| 239 | // Enable fullfsync for the platforms that use it |
| 240 | SetPragma(m_db, "fullfsync", "true", "Failed to enable fullfsync"); |
| 241 | |
| 242 | if (gArgs.GetBoolArg("-unsafesqlitesync", false)) { |
| 243 | // Use normal synchronous mode for the journal |
| 244 | LogPrintf("WARNING SQLite is configured to not wait for data to be flushed to disk. Data loss and corruption may occur.\n"); |
| 245 | SetPragma(m_db, "synchronous", "OFF", "Failed to set synchronous mode to OFF"); |
| 246 | } |
| 247 | |
| 248 | // Make the table for our key-value pairs |
| 249 | // First check that the main table exists |
| 250 | sqlite3_stmt* check_main_stmt{nullptr}; |
| 251 | ret = sqlite3_prepare_v2(m_db, "SELECT name FROM sqlite_master WHERE type='table' AND name='main'", -1, &check_main_stmt, nullptr); |
| 252 | if (ret != SQLITE_OK) { |
| 253 | throw std::runtime_error(strprintf("SQLiteDatabase: Failed to prepare statement to check table existence: %s\n", sqlite3_errstr(ret))); |
| 254 | } |
| 255 | ret = sqlite3_step(check_main_stmt); |
| 256 | if (sqlite3_finalize(check_main_stmt) != SQLITE_OK) { |
| 257 | throw std::runtime_error(strprintf("SQLiteDatabase: Failed to finalize statement checking table existence: %s\n", sqlite3_errstr(ret))); |
| 258 | } |
nothing calls this directly
no test coverage detected