From 2deb6446ebcd7cdb016f118bbd170e3dd0bc4fc3 Mon Sep 17 00:00:00 2001 From: Martin Rotter Date: Fri, 25 Aug 2023 11:30:59 +0200 Subject: [PATCH] fix wrong stuff when doing backup of DB - was not working because of WAL journalling mode --- src/librssguard/database/sqlitedriver.cpp | 127 ++++++------------ src/librssguard/database/sqlitedriver.h | 14 +- src/librssguard/miscellaneous/application.cpp | 8 +- 3 files changed, 60 insertions(+), 89 deletions(-) diff --git a/src/librssguard/database/sqlitedriver.cpp b/src/librssguard/database/sqlitedriver.cpp index 64b88572a..00e7b95b8 100644 --- a/src/librssguard/database/sqlitedriver.cpp +++ b/src/librssguard/database/sqlitedriver.cpp @@ -5,12 +5,6 @@ #include "exceptions/applicationexception.h" #include "miscellaneous/application.h" -#if defined(SYSTEM_SQLITE3) -#include -#else -#include "3rd-party/sqlite/sqlite3.h" -#endif - #include #include #include @@ -44,25 +38,25 @@ QString SqliteDriver::ddlFilePrefix() const { return QSL("sqlite"); } -int loadOrSaveDb(sqlite3* pInMemory, const char* zFilename, int isSave) { - int rc; /* Function return code */ - sqlite3* pFile; /* Database connection opened on zFilename */ - sqlite3_backup* pBackup; /* Backup object used to copy data */ - sqlite3* pTo; /* Database to copy to (pFile or pInMemory) */ - sqlite3* pFrom; /* Database to copy from (pFile or pInMemory) */ +int SqliteDriver::loadOrSaveDbInMemoryDb(sqlite3* in_memory_db, const char* db_filename, bool save) { + int rc; /* Function return code */ + sqlite3* p_file; /* Database connection opened on zFilename */ + sqlite3_backup* p_backup; /* Backup object used to copy data */ + sqlite3* p_to; /* Database to copy to (pFile or pInMemory) */ + sqlite3* p_from; /* Database to copy from (pFile or pInMemory) */ /* Open the database file identified by zFilename. Exit early if this fails ** for any reason. */ - rc = sqlite3_open(zFilename, &pFile); - if (rc == SQLITE_OK) { + rc = sqlite3_open(db_filename, &p_file); + if (rc == SQLITE_OK) { /* If this is a 'load' operation (isSave==0), then data is copied ** from the database file just opened to database pInMemory. ** Otherwise, if this is a 'save' operation (isSave==1), then data ** is copied from pInMemory to pFile. Set the variables pFrom and ** pTo accordingly. */ - pFrom = (isSave ? pInMemory : pFile); - pTo = (isSave ? pFile : pInMemory); + p_from = (save ? in_memory_db : p_file); + p_to = (save ? p_file : in_memory_db); /* Set up the backup procedure to copy from the "main" database of ** connection pFile to the main database of connection pInMemory. @@ -76,19 +70,19 @@ int loadOrSaveDb(sqlite3* pInMemory, const char* zFilename, int isSave) { ** connection pTo. If no error occurred, then the error code belonging ** to pTo is set to SQLITE_OK. */ - pBackup = sqlite3_backup_init(pTo, "main", pFrom, "main"); - if (pBackup) { - (void)sqlite3_backup_step(pBackup, -1); - (void)sqlite3_backup_finish(pBackup); + p_backup = sqlite3_backup_init(p_to, "main", p_from, "main"); + if (p_backup) { + (void)sqlite3_backup_step(p_backup, -1); + (void)sqlite3_backup_finish(p_backup); } - rc = sqlite3_errcode(pTo); + rc = sqlite3_errcode(p_to); } - sqlite3_db_cacheflush(pFile); + sqlite3_db_cacheflush(p_file); /* Close the database connection opened on database file zFilename ** and return the result of this function. */ - (void)sqlite3_close(pFile); + (void)sqlite3_close(p_file); return rc; } @@ -96,69 +90,28 @@ bool SqliteDriver::saveDatabase() { if (!m_inMemoryDatabase) { return true; } + else { + qDebugNN << LOGSEC_DB << "Saving in-memory working database back to persistent file-based storage."; - qDebugNN << LOGSEC_DB << "Saving in-memory working database back to persistent file-based storage."; + QSqlDatabase database = connection(QSL("SaveFromMemory"), DatabaseDriver::DesiredStorageType::StrictlyInMemory); + const QDir db_path(m_databaseFilePath); + QFile db_file(db_path.absoluteFilePath(QSL(APP_DB_SQLITE_FILE))); + QVariant v = database.driver()->handle(); - QSqlDatabase database = connection(QSL("SaveFromMemory"), DatabaseDriver::DesiredStorageType::StrictlyInMemory); - const QDir db_path(m_databaseFilePath); - QFile db_file(db_path.absoluteFilePath(QSL(APP_DB_SQLITE_FILE))); - QVariant v = database.driver()->handle(); + if (v.isValid() && (qstrcmp(v.typeName(), "sqlite3*") == 0)) { + // v.data() returns a pointer to the handle + sqlite3* handle = *static_cast(v.data()); - if (v.isValid() && (qstrcmp(v.typeName(), "sqlite3*") == 0)) { - // v.data() returns a pointer to the handle - sqlite3* handle = *static_cast(v.data()); - if (handle) { - loadOrSaveDb(handle, QDir::toNativeSeparators(db_file.fileName()).toStdString().c_str(), 1); + if (handle != nullptr) { + loadOrSaveDbInMemoryDb(handle, QDir::toNativeSeparators(db_file.fileName()).toStdString().c_str(), 1); + } + else { + throw ApplicationException(tr("cannot get native 'sqlite3' DB handle")); + } } + + return true; } - - return true; - /* - QSqlQuery copy_contents(database); - - // Attach database. - copy_contents.exec(QString(QSL("ATTACH DATABASE '%1' AS 'storage';")).arg(file_database.databaseName())); - - // Copy all stuff. - QStringList tables; - - if (copy_contents.exec(QSL("SELECT name FROM storage.sqlite_master WHERE type='table';"))) { - while (copy_contents.next()) { - tables.append(copy_contents.value(0).toString()); - } - } - else { - qFatal("Cannot obtain list of table names from file-base SQLite database."); - } - - for (const QString& table : tables) { - if (copy_contents.exec(QString(QSL("DELETE FROM storage.%1;")).arg(table))) { - qDebugNN << LOGSEC_DB << "Cleaning old data from 'storage." << table << "'."; - } - else { - qCriticalNN << LOGSEC_DB << "Failed to clean old data from 'storage." << table << "', error: '" - << copy_contents.lastError().text() << "'."; - } - - if (copy_contents.exec(QString(QSL("INSERT INTO storage.%1 SELECT * FROM main.%1;")).arg(table))) { - qDebugNN << LOGSEC_DB << "Copying new data from 'main." << table << "'."; - } - else { - qCriticalNN << LOGSEC_DB << "Failed to copy new data from 'main." << table << "', error: '" - << copy_contents.lastError().text() << "'."; - } - } - - // Detach database and finish. - if (copy_contents.exec(QSL("DETACH 'storage'"))) { - qDebugNN << LOGSEC_DB << "Detaching persistent SQLite file."; - } - else { - qCriticalNN << LOGSEC_DB << "Failed to detach SQLite file, error: '" << copy_contents.lastError().text() << "'."; - } - - copy_contents.finish(); - return true;*/ } QSqlDatabase SqliteDriver::connection(const QString& connection_name, DesiredStorageType desired_type) { @@ -394,13 +347,13 @@ QString SqliteDriver::databaseFilePath() const { void SqliteDriver::setPragmas(QSqlQuery& query) { query.exec(QSL("PRAGMA encoding = \"UTF-8\"")); - query.exec(QSL("PRAGMA synchronous = OFF")); - // query.exec(QSL("PRAGMA journal_mode = MEMORY")); - query.exec(QSL("PRAGMA page_size = 4096")); - query.exec(QSL("PRAGMA cache_size = 16384")); + query.exec(QSL("PRAGMA page_size = 32768")); + query.exec(QSL("PRAGMA cache_size = 32768")); + query.exec(QSL("PRAGMA mmap_size = 100000000")); query.exec(QSL("PRAGMA count_changes = OFF")); query.exec(QSL("PRAGMA temp_store = MEMORY")); - query.exec(QSL("PRAGMA journal_mode = WAL")); + query.exec(QSL("PRAGMA synchronous = OFF")); + query.exec(QSL("PRAGMA journal_mode = MEMORY")); } qint64 SqliteDriver::databaseDataSize() { @@ -436,6 +389,8 @@ QString SqliteDriver::qtDriverCode() const { } void SqliteDriver::backupDatabase(const QString& backup_folder, const QString& backup_name) { + qDebugNN << LOGSEC_DB << "Creating SQLite DB backup."; + saveDatabase(); if (!IOFactory::copyFile(databaseFilePath(), diff --git a/src/librssguard/database/sqlitedriver.h b/src/librssguard/database/sqlitedriver.h index f61224692..66d777157 100644 --- a/src/librssguard/database/sqlitedriver.h +++ b/src/librssguard/database/sqlitedriver.h @@ -5,8 +5,14 @@ #include "database/databasedriver.h" +#if defined(SYSTEM_SQLITE3) +#include +#else +#include "3rd-party/sqlite/sqlite3.h" +#endif + class SqliteDriver : public DatabaseDriver { - Q_OBJECT + Q_OBJECT public: explicit SqliteDriver(bool in_memory, QObject* parent = nullptr); @@ -19,7 +25,8 @@ class SqliteDriver : public DatabaseDriver { virtual bool initiateRestoration(const QString& database_package_file); virtual bool finishRestoration(); virtual QSqlDatabase connection(const QString& connection_name, - DatabaseDriver::DesiredStorageType desired_type = DatabaseDriver::DesiredStorageType::FromSettings); + DatabaseDriver::DesiredStorageType desired_type = + DatabaseDriver::DesiredStorageType::FromSettings); virtual qint64 databaseDataSize(); virtual QString humanDriverType() const; virtual QString qtDriverCode() const; @@ -32,6 +39,9 @@ class SqliteDriver : public DatabaseDriver { void setPragmas(QSqlQuery& query); QString databaseFilePath() const; + // Uses native "sqlite3" handle to save or load in-memory DB from/to file. + int loadOrSaveDbInMemoryDb(sqlite3* in_memory_db, const char* db_filename, bool save); + private: bool m_inMemoryDatabase; QString m_databaseFilePath; diff --git a/src/librssguard/miscellaneous/application.cpp b/src/librssguard/miscellaneous/application.cpp index 0d0571456..af55ddcc2 100644 --- a/src/librssguard/miscellaneous/application.cpp +++ b/src/librssguard/miscellaneous/application.cpp @@ -790,7 +790,13 @@ void Application::onAboutToQuit() { } qApp->feedReader()->quit(); - database()->driver()->saveDatabase(); + + try { + database()->driver()->saveDatabase(); + } + catch (const ApplicationException& ex) { + qCriticalNN << LOGSEC_DB << "Error when saving DB:" << QUOTE_W_SPACE_DOT(ex.message()); + } if (mainForm() != nullptr) { mainForm()->saveSize();