fix wrong stuff when doing backup of DB - was not working because of WAL journalling mode

This commit is contained in:
Martin Rotter 2023-08-25 11:30:59 +02:00
parent 96920ab723
commit 2deb6446eb
3 changed files with 60 additions and 89 deletions

View File

@ -5,12 +5,6 @@
#include "exceptions/applicationexception.h" #include "exceptions/applicationexception.h"
#include "miscellaneous/application.h" #include "miscellaneous/application.h"
#if defined(SYSTEM_SQLITE3)
#include <sqlite3.h>
#else
#include "3rd-party/sqlite/sqlite3.h"
#endif
#include <QDir> #include <QDir>
#include <QSqlDriver> #include <QSqlDriver>
#include <QSqlError> #include <QSqlError>
@ -44,25 +38,25 @@ QString SqliteDriver::ddlFilePrefix() const {
return QSL("sqlite"); return QSL("sqlite");
} }
int loadOrSaveDb(sqlite3* pInMemory, const char* zFilename, int isSave) { int SqliteDriver::loadOrSaveDbInMemoryDb(sqlite3* in_memory_db, const char* db_filename, bool save) {
int rc; /* Function return code */ int rc; /* Function return code */
sqlite3* pFile; /* Database connection opened on zFilename */ sqlite3* p_file; /* Database connection opened on zFilename */
sqlite3_backup* pBackup; /* Backup object used to copy data */ sqlite3_backup* p_backup; /* Backup object used to copy data */
sqlite3* pTo; /* Database to copy to (pFile or pInMemory) */ sqlite3* p_to; /* Database to copy to (pFile or pInMemory) */
sqlite3* pFrom; /* Database to copy from (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 /* Open the database file identified by zFilename. Exit early if this fails
** for any reason. */ ** for any reason. */
rc = sqlite3_open(zFilename, &pFile); rc = sqlite3_open(db_filename, &p_file);
if (rc == SQLITE_OK) {
if (rc == SQLITE_OK) {
/* If this is a 'load' operation (isSave==0), then data is copied /* If this is a 'load' operation (isSave==0), then data is copied
** from the database file just opened to database pInMemory. ** from the database file just opened to database pInMemory.
** Otherwise, if this is a 'save' operation (isSave==1), then data ** Otherwise, if this is a 'save' operation (isSave==1), then data
** is copied from pInMemory to pFile. Set the variables pFrom and ** is copied from pInMemory to pFile. Set the variables pFrom and
** pTo accordingly. */ ** pTo accordingly. */
pFrom = (isSave ? pInMemory : pFile); p_from = (save ? in_memory_db : p_file);
pTo = (isSave ? pFile : pInMemory); p_to = (save ? p_file : in_memory_db);
/* Set up the backup procedure to copy from the "main" database of /* Set up the backup procedure to copy from the "main" database of
** connection pFile to the main database of connection pInMemory. ** 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 ** connection pTo. If no error occurred, then the error code belonging
** to pTo is set to SQLITE_OK. ** to pTo is set to SQLITE_OK.
*/ */
pBackup = sqlite3_backup_init(pTo, "main", pFrom, "main"); p_backup = sqlite3_backup_init(p_to, "main", p_from, "main");
if (pBackup) { if (p_backup) {
(void)sqlite3_backup_step(pBackup, -1); (void)sqlite3_backup_step(p_backup, -1);
(void)sqlite3_backup_finish(pBackup); (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 /* Close the database connection opened on database file zFilename
** and return the result of this function. */ ** and return the result of this function. */
(void)sqlite3_close(pFile); (void)sqlite3_close(p_file);
return rc; return rc;
} }
@ -96,69 +90,28 @@ bool SqliteDriver::saveDatabase() {
if (!m_inMemoryDatabase) { if (!m_inMemoryDatabase) {
return true; 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); if (v.isValid() && (qstrcmp(v.typeName(), "sqlite3*") == 0)) {
const QDir db_path(m_databaseFilePath); // v.data() returns a pointer to the handle
QFile db_file(db_path.absoluteFilePath(QSL(APP_DB_SQLITE_FILE))); sqlite3* handle = *static_cast<sqlite3**>(v.data());
QVariant v = database.driver()->handle();
if (v.isValid() && (qstrcmp(v.typeName(), "sqlite3*") == 0)) { if (handle != nullptr) {
// v.data() returns a pointer to the handle loadOrSaveDbInMemoryDb(handle, QDir::toNativeSeparators(db_file.fileName()).toStdString().c_str(), 1);
sqlite3* handle = *static_cast<sqlite3**>(v.data()); }
if (handle) { else {
loadOrSaveDb(handle, QDir::toNativeSeparators(db_file.fileName()).toStdString().c_str(), 1); 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) { QSqlDatabase SqliteDriver::connection(const QString& connection_name, DesiredStorageType desired_type) {
@ -394,13 +347,13 @@ QString SqliteDriver::databaseFilePath() const {
void SqliteDriver::setPragmas(QSqlQuery& query) { void SqliteDriver::setPragmas(QSqlQuery& query) {
query.exec(QSL("PRAGMA encoding = \"UTF-8\"")); query.exec(QSL("PRAGMA encoding = \"UTF-8\""));
query.exec(QSL("PRAGMA synchronous = OFF")); query.exec(QSL("PRAGMA page_size = 32768"));
// query.exec(QSL("PRAGMA journal_mode = MEMORY")); query.exec(QSL("PRAGMA cache_size = 32768"));
query.exec(QSL("PRAGMA page_size = 4096")); query.exec(QSL("PRAGMA mmap_size = 100000000"));
query.exec(QSL("PRAGMA cache_size = 16384"));
query.exec(QSL("PRAGMA count_changes = OFF")); query.exec(QSL("PRAGMA count_changes = OFF"));
query.exec(QSL("PRAGMA temp_store = MEMORY")); 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() { qint64 SqliteDriver::databaseDataSize() {
@ -436,6 +389,8 @@ QString SqliteDriver::qtDriverCode() const {
} }
void SqliteDriver::backupDatabase(const QString& backup_folder, const QString& backup_name) { void SqliteDriver::backupDatabase(const QString& backup_folder, const QString& backup_name) {
qDebugNN << LOGSEC_DB << "Creating SQLite DB backup.";
saveDatabase(); saveDatabase();
if (!IOFactory::copyFile(databaseFilePath(), if (!IOFactory::copyFile(databaseFilePath(),

View File

@ -5,8 +5,14 @@
#include "database/databasedriver.h" #include "database/databasedriver.h"
#if defined(SYSTEM_SQLITE3)
#include <sqlite3.h>
#else
#include "3rd-party/sqlite/sqlite3.h"
#endif
class SqliteDriver : public DatabaseDriver { class SqliteDriver : public DatabaseDriver {
Q_OBJECT Q_OBJECT
public: public:
explicit SqliteDriver(bool in_memory, QObject* parent = nullptr); 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 initiateRestoration(const QString& database_package_file);
virtual bool finishRestoration(); virtual bool finishRestoration();
virtual QSqlDatabase connection(const QString& connection_name, 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 qint64 databaseDataSize();
virtual QString humanDriverType() const; virtual QString humanDriverType() const;
virtual QString qtDriverCode() const; virtual QString qtDriverCode() const;
@ -32,6 +39,9 @@ class SqliteDriver : public DatabaseDriver {
void setPragmas(QSqlQuery& query); void setPragmas(QSqlQuery& query);
QString databaseFilePath() const; 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: private:
bool m_inMemoryDatabase; bool m_inMemoryDatabase;
QString m_databaseFilePath; QString m_databaseFilePath;

View File

@ -790,7 +790,13 @@ void Application::onAboutToQuit() {
} }
qApp->feedReader()->quit(); 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) { if (mainForm() != nullptr) {
mainForm()->saveSize(); mainForm()->saveSize();