// This file is part of RSS Guard. // // Copyright (C) 2011-2016 by Martin Rotter // // RSS Guard is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // RSS Guard is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with RSS Guard. If not, see . #include "miscellaneous/databasefactory.h" #include "miscellaneous/iofactory.h" #include "miscellaneous/application.h" #include "miscellaneous/textfactory.h" #include "gui/messagebox.h" #include #include #include #include DatabaseFactory::DatabaseFactory(QObject *parent) : QObject(parent), m_mysqlDatabaseInitialized(false), m_sqliteFileBasedDatabaseinitialized(false), m_sqliteInMemoryDatabaseInitialized(false) { setObjectName(QSL("DatabaseFactory")); determineDriver(); } DatabaseFactory::~DatabaseFactory() { } qint64 DatabaseFactory::getDatabaseFileSize() const { if (m_activeDatabaseDriver == SQLITE || m_activeDatabaseDriver == SQLITE_MEMORY) { return QFileInfo(sqliteDatabaseFilePath()).size(); } else { return 0; } } qint64 DatabaseFactory::getDatabaseDataSize() const { if (m_activeDatabaseDriver == SQLITE || m_activeDatabaseDriver == SQLITE_MEMORY) { QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); qint64 result = 1; QSqlQuery query(database); if (query.exec(QSL("PRAGMA page_count;"))) { query.next(); result *= query.value(0).value(); } else { return 0; } if (query.exec(QSL("PRAGMA page_size;"))) { query.next(); result *= query.value(0).value(); } else { return 0; } return result; } else if (m_activeDatabaseDriver == MYSQL) { QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); qint64 result = 1; QSqlQuery query(database); if (query.exec("SELECT Round(Sum(data_length + index_length), 1) " "FROM information_schema.tables " "GROUP BY table_schema;")) { while (query.next()) { result *= query.value(0).value(); } return result; } else { return 0; } } else { return 0; } } DatabaseFactory::MySQLError DatabaseFactory::mysqlTestConnection(const QString &hostname, int port, const QString &w_database, const QString &username, const QString &password) { QSqlDatabase database = QSqlDatabase::addDatabase(APP_DB_MYSQL_DRIVER, APP_DB_MYSQL_TEST); database.setHostName(hostname); database.setPort(port); database.setUserName(username); database.setPassword(password); database.setDatabaseName(w_database); if (database.open() && !database.lastError().isValid()) { QSqlQuery query(QSL("SELECT version();"), database); if (!query.lastError().isValid() && query.next()) { qDebug("Checked MySQL database, version is '%s'.", qPrintable(query.value(0).toString())); // Connection succeeded, clean up the mess and return OK status. database.close(); return MySQLOk; } else { database.close(); return MySQLUnknownError; } } else if (database.lastError().isValid()) { // Connection failed, do cleanup and return specific error code. return static_cast(database.lastError().number()); } else { return MySQLUnknownError; } } QString DatabaseFactory::mysqlInterpretErrorCode(MySQLError error_code) const { switch (error_code) { case MySQLOk: return tr("MySQL server works as expected."); case MySQLUnknownDatabase: return tr("Selected database does not exist (yet). It will be created. It's okay."); case MySQLCantConnect: case MySQLConnectionError: case MySQLUnknownHost: return tr("No MySQL server is running in the target destination."); case MySQLAccessDenied: //: Access to MySQL server was denied. return tr("Access denied. Invalid username or password used."); default: //: Unknown MySQL error arised. return tr("Unknown error."); } } bool DatabaseFactory::initiateRestoration(const QString &database_backup_file_path) { switch (m_activeDatabaseDriver) { case SQLITE: case SQLITE_MEMORY: return IOFactory::copyFile(database_backup_file_path, m_sqliteDatabaseFilePath + QDir::separator() + BACKUP_NAME_DATABASE + BACKUP_SUFFIX_DATABASE); default: return false; } } void DatabaseFactory::finishRestoration() { if (m_activeDatabaseDriver != SQLITE && m_activeDatabaseDriver != SQLITE_MEMORY) { return; } const QString backup_database_file = m_sqliteDatabaseFilePath + QDir::separator() + BACKUP_NAME_DATABASE + BACKUP_SUFFIX_DATABASE; if (QFile::exists(backup_database_file)) { qWarning("Backup database file '%s' was detected. Restoring it.", qPrintable(QDir::toNativeSeparators(backup_database_file))); if (IOFactory::copyFile(backup_database_file, m_sqliteDatabaseFilePath + QDir::separator() + APP_DB_SQLITE_FILE)) { QFile::remove(backup_database_file); qDebug("Database file was restored successully."); } else { qCritical("Database file was NOT restored due to error when copying the file."); } } } void DatabaseFactory::sqliteAssemblyDatabaseFilePath() { if (qApp->settings()->type() == SettingsProperties::Portable) { m_sqliteDatabaseFilePath = qApp->applicationDirPath() + QDir::separator() + QString(APP_DB_SQLITE_PATH); } else { m_sqliteDatabaseFilePath = qApp->homeFolderPath() + QDir::separator() + QString(APP_LOW_H_NAME) + QDir::separator() + QString(APP_DB_SQLITE_PATH); } } QSqlDatabase DatabaseFactory::sqliteInitializeInMemoryDatabase() { QSqlDatabase database = QSqlDatabase::addDatabase(APP_DB_SQLITE_DRIVER); database.setDatabaseName(QSL(":memory:")); if (!database.open()) { qFatal("In-memory SQLite database was NOT opened. Delivered error message: '%s'", qPrintable(database.lastError().text())); } else { QSqlQuery query_db(database); query_db.setForwardOnly(true); query_db.exec(QSL("PRAGMA encoding = \"UTF-8\"")); query_db.exec(QSL("PRAGMA synchronous = OFF")); query_db.exec(QSL("PRAGMA journal_mode = MEMORY")); query_db.exec(QSL("PRAGMA page_size = 4096")); query_db.exec(QSL("PRAGMA cache_size = 16384")); query_db.exec(QSL("PRAGMA count_changes = OFF")); query_db.exec(QSL("PRAGMA temp_store = MEMORY")); // Sample query which checks for existence of tables. query_db.exec(QSL("SELECT inf_value FROM Information WHERE inf_key = 'schema_version'")); if (query_db.lastError().isValid()) { qWarning("Error occurred. In-memory SQLite database is not initialized. Initializing now."); QFile file_init(APP_MISC_PATH + QDir::separator() + APP_DB_SQLITE_INIT); if (!file_init.open(QIODevice::ReadOnly | QIODevice::Text)) { // Database initialization file not opened. HUGE problem. qFatal("In-memory SQLite database initialization file '%s' from directory '%s' was not found. In-memory database is uninitialized.", APP_DB_SQLITE_INIT, qPrintable(APP_MISC_PATH)); } const QStringList statements = QString(file_init.readAll()).split(APP_DB_COMMENT_SPLIT, QString::SkipEmptyParts); database.transaction(); foreach(const QString &statement, statements) { query_db.exec(statement); if (query_db.lastError().isValid()) { qFatal("In-memory SQLite database initialization failed. Initialization script '%s' is not correct.", APP_DB_SQLITE_INIT); } } database.commit(); qDebug("In-memory SQLite database backend should be ready now."); } else { query_db.next(); qDebug("In-memory SQLite database connection seems to be established."); qDebug("In-memory SQLite database has version '%s'.", qPrintable(query_db.value(0).toString())); } // Loading messages from file-based database. QSqlDatabase file_database = sqliteConnection(objectName(), StrictlyFileBased); QSqlQuery copy_contents(database); // Attach database. copy_contents.exec(QString("ATTACH DATABASE '%1' AS 'storage';").arg(file_database.databaseName())); // Copy all stuff. // WARNING: All tables belong here. 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."); } foreach (const QString &table, tables) { copy_contents.exec(QString("INSERT INTO main.%1 SELECT * FROM storage.%1;").arg(table)); } qDebug("Copying data from file-based database into working in-memory database."); // Detach database and finish. copy_contents.exec(QSL("DETACH 'storage'")); copy_contents.finish(); query_db.finish(); } // Everything is initialized now. m_sqliteInMemoryDatabaseInitialized = true; return database; } QSqlDatabase DatabaseFactory::sqliteInitializeFileBasedDatabase(const QString &connection_name) { finishRestoration(); // Prepare file paths. const QDir db_path(m_sqliteDatabaseFilePath); QFile db_file(db_path.absoluteFilePath(APP_DB_SQLITE_FILE)); // Check if database directory exists. if (!db_path.exists()) { if (!db_path.mkpath(db_path.absolutePath())) { // Failure when create database file path. qFatal("Directory '%s' for SQLite database file '%s' was NOT created." "This is HUGE problem.", qPrintable(db_path.absolutePath()), qPrintable(db_file.symLinkTarget())); } } // Folders are created. Create new QSQLDatabase object. QSqlDatabase database; database = QSqlDatabase::addDatabase(APP_DB_SQLITE_DRIVER, connection_name); database.setDatabaseName(db_file.fileName()); if (!database.open()) { qFatal("File-based SQLite database was NOT opened. Delivered error message: '%s'", qPrintable(database.lastError().text())); } else { QSqlQuery query_db(database); query_db.setForwardOnly(true); query_db.exec(QSL("PRAGMA encoding = \"UTF-8\"")); query_db.exec(QSL("PRAGMA synchronous = OFF")); query_db.exec(QSL("PRAGMA journal_mode = MEMORY")); query_db.exec(QSL("PRAGMA page_size = 4096")); query_db.exec(QSL("PRAGMA cache_size = 16384")); query_db.exec(QSL("PRAGMA count_changes = OFF")); query_db.exec(QSL("PRAGMA temp_store = MEMORY")); // Sample query which checks for existence of tables. if (!query_db.exec(QSL("SELECT inf_value FROM Information WHERE inf_key = 'schema_version'"))) { qWarning("Error occurred. File-based SQLite database is not initialized. Initializing now."); QFile file_init(APP_MISC_PATH + QDir::separator() + APP_DB_SQLITE_INIT); if (!file_init.open(QIODevice::ReadOnly | QIODevice::Text)) { // Database initialization file not opened. HUGE problem. qFatal("SQLite database initialization file '%s' from directory '%s' was not found. File-based database is uninitialized.", APP_DB_SQLITE_INIT, qPrintable(APP_MISC_PATH)); } const QStringList statements = QString(file_init.readAll()).split(APP_DB_COMMENT_SPLIT, QString::SkipEmptyParts); database.transaction(); foreach(const QString &statement, statements) { query_db.exec(statement); if (query_db.lastError().isValid()) { qFatal("File-based SQLite database initialization failed. Initialization script '%s' is not correct.", APP_DB_SQLITE_INIT); } } database.commit(); query_db.finish(); qDebug("File-based SQLite database backend should be ready now."); } else { query_db.next(); const QString installed_db_schema = query_db.value(0).toString(); query_db.finish(); if (installed_db_schema < APP_DB_SCHEMA_VERSION) { if (sqliteUpdateDatabaseSchema(database, installed_db_schema)) { qDebug("Database schema was updated from '%s' to '%s' successully or it is already up to date.", qPrintable(installed_db_schema), APP_DB_SCHEMA_VERSION); } else { qFatal("Database schema was not updated from '%s' to '%s' successully.", qPrintable(installed_db_schema), APP_DB_SCHEMA_VERSION); } } qDebug("File-based SQLite database connection '%s' to file '%s' seems to be established.", qPrintable(connection_name), qPrintable(QDir::toNativeSeparators(database.databaseName()))); qDebug("File-based SQLite database has version '%s'.", qPrintable(installed_db_schema)); } } // Everything is initialized now. m_sqliteFileBasedDatabaseinitialized = true; return database; } QString DatabaseFactory::sqliteDatabaseFilePath() const { return m_sqliteDatabaseFilePath + QDir::separator() + APP_DB_SQLITE_FILE; } bool DatabaseFactory::sqliteUpdateDatabaseSchema(QSqlDatabase database, const QString &source_db_schema_version) { int working_version = QString(source_db_schema_version).remove('.').toInt(); const int current_version = QString(APP_DB_SCHEMA_VERSION).remove('.').toInt(); // Now, it would be good to create backup of SQLite DB file. if (IOFactory::copyFile(sqliteDatabaseFilePath(), sqliteDatabaseFilePath() + ".bak")) { qDebug("Creating backup of SQLite DB file."); } else { qFatal("Creation of backup SQLite DB file failed."); } while (working_version != current_version) { const QString update_file_name = QString(APP_MISC_PATH) + QDir::separator() + QString(APP_DB_UPDATE_FILE_PATTERN).arg(QSL("sqlite"), QString::number(working_version), QString::number(working_version + 1)); if (!QFile::exists(update_file_name)) { qFatal("Updating of database schema failed. File '%s' does not exist.", qPrintable(QDir::toNativeSeparators(update_file_name))); } QFile update_file_handle(update_file_name); if (!update_file_handle.open(QIODevice::Text | QIODevice::ReadOnly | QIODevice::Unbuffered)) { qFatal("Updating of database schema failed. File '%s' cannot be opened.", qPrintable(QDir::toNativeSeparators(update_file_name))); } const QStringList statements = QString(update_file_handle.readAll()).split(APP_DB_COMMENT_SPLIT, QString::SkipEmptyParts); foreach (const QString &statement, statements) { QSqlQuery query = database.exec(statement); if (query.lastError().isValid()) { qFatal("Query for updating database schema failed: '%s'.", qPrintable(query.lastError().text())); } } // Increment the version. qDebug("Updating database schema: '%d' -> '%d'.", working_version, working_version + 1); working_version++; } return true; } bool DatabaseFactory::mysqlUpdateDatabaseSchema(QSqlDatabase database, const QString &source_db_schema_version) { int working_version = QString(source_db_schema_version).remove('.').toInt(); const int current_version = QString(APP_DB_SCHEMA_VERSION).remove('.').toInt(); while (working_version != current_version) { const QString update_file_name = QString(APP_MISC_PATH) + QDir::separator() + QString(APP_DB_UPDATE_FILE_PATTERN).arg(QSL("mysql"), QString::number(working_version), QString::number(working_version + 1)); if (!QFile::exists(update_file_name)) { qFatal("Updating of database schema failed. File '%s' does not exist.", qPrintable(QDir::toNativeSeparators(update_file_name))); } QFile update_file_handle(update_file_name); if (!update_file_handle.open(QIODevice::Text | QIODevice::ReadOnly | QIODevice::Unbuffered)) { qFatal("Updating of database schema failed. File '%s' cannot be opened.", qPrintable(QDir::toNativeSeparators(update_file_name))); } QStringList statements = QString(update_file_handle.readAll()).split(APP_DB_COMMENT_SPLIT, QString::SkipEmptyParts); foreach (const QString &statement, statements) { QSqlQuery query = database.exec(statement); if (query.lastError().isValid()) { qFatal("Query for updating database schema failed: '%s'.", qPrintable(query.lastError().text())); } } // Increment the version. qDebug("Updating database schema: '%d' -> '%d'.", working_version, working_version + 1); working_version++; } return true; } QSqlDatabase DatabaseFactory::connection(const QString &connection_name, DesiredType desired_type) { switch (m_activeDatabaseDriver) { case MYSQL: return mysqlConnection(connection_name); case SQLITE: case SQLITE_MEMORY: default: return sqliteConnection(connection_name, desired_type); } } QString DatabaseFactory::humanDriverName(DatabaseFactory::UsedDriver driver) const { switch (driver) { case MYSQL: return tr("MySQL/MariaDB (dedicated database)"); case SQLITE: case SQLITE_MEMORY: default: return tr("SQLite (embedded database)"); } } QString DatabaseFactory::humanDriverName(const QString &driver_code) const { if (driver_code == APP_DB_SQLITE_DRIVER) { return humanDriverName(SQLITE); } else if (driver_code == APP_DB_MYSQL_DRIVER) { return humanDriverName(MYSQL); } else { return humanDriverName(SQLITE); } } void DatabaseFactory::removeConnection(const QString &connection_name) { qDebug("Removing database connection '%s'.", qPrintable(connection_name)); QSqlDatabase::removeDatabase(connection_name); } void DatabaseFactory::sqliteSaveMemoryDatabase() { qDebug("Saving in-memory working database back to persistent file-based storage."); QSqlDatabase database = sqliteConnection(objectName(), StrictlyInMemory); QSqlDatabase file_database = sqliteConnection(objectName(), StrictlyFileBased); QSqlQuery copy_contents(database); // Attach database. copy_contents.exec(QString(QSL("ATTACH DATABASE '%1' AS 'storage';")).arg(file_database.databaseName())); // Copy all stuff. // WARNING: All tables belong here. 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."); } foreach (const QString &table, tables) { copy_contents.exec(QString(QSL("DELETE FROM storage.%1;")).arg(table)); copy_contents.exec(QString(QSL("INSERT INTO storage.%1 SELECT * FROM main.%1;")).arg(table)); } // Detach database and finish. copy_contents.exec(QSL("DETACH 'storage'")); copy_contents.finish(); } void DatabaseFactory::determineDriver() { const QString db_driver = qApp->settings()->value(GROUP(Database), SETTING(Database::ActiveDriver)).toString(); if (db_driver == APP_DB_MYSQL_DRIVER && QSqlDatabase::isDriverAvailable(APP_DB_SQLITE_DRIVER)) { // User wants to use MySQL and MySQL is actually available. Use it. m_activeDatabaseDriver = MYSQL; qDebug("Working database source was as MySQL database."); } else { // User wants to use SQLite, which is always available. Check if file-based // or in-memory database will be used. if (qApp->settings()->value(GROUP(Database), SETTING(Database::UseInMemory)).toBool()) { // Use in-memory SQLite database. m_activeDatabaseDriver = SQLITE_MEMORY; qDebug("Working database source was determined as SQLite in-memory database."); } else { // Use strictly file-base SQLite database. m_activeDatabaseDriver = SQLITE; qDebug("Working database source was determined as SQLite file-based database."); } sqliteAssemblyDatabaseFilePath(); } } DatabaseFactory::UsedDriver DatabaseFactory::activeDatabaseDriver() const { return m_activeDatabaseDriver; } QSqlDatabase DatabaseFactory::mysqlConnection(const QString &connection_name) { if (!m_mysqlDatabaseInitialized) { // Return initialized database. return mysqlInitializeDatabase(connection_name); } else { QSqlDatabase database; if (QSqlDatabase::contains(connection_name)) { qDebug("MySQL connection '%s' is already active.", qPrintable(connection_name)); // This database connection was added previously, no need to // setup its properties. database = QSqlDatabase::database(connection_name); } else { // Database connection with this name does not exist // yet, add it and set it up. database = QSqlDatabase::addDatabase(APP_DB_MYSQL_DRIVER, connection_name); database.setHostName(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLHostname)).toString()); database.setPort(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLPort)).toInt()); database.setUserName(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLUsername)).toString()); database.setPassword(TextFactory::decrypt(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLPassword)).toString())); database.setDatabaseName(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLDatabase)).toString()); } if (!database.isOpen() && !database.open()) { qFatal("MySQL database was NOT opened. Delivered error message: '%s'.", qPrintable(database.lastError().text())); } else { qDebug("MySQL database connection '%s' to file '%s' seems to be established.", qPrintable(connection_name), qPrintable(QDir::toNativeSeparators(database.databaseName()))); } return database; } } QSqlDatabase DatabaseFactory::mysqlInitializeDatabase(const QString &connection_name) { // Folders are created. Create new QSQLDatabase object. QSqlDatabase database = QSqlDatabase::addDatabase(APP_DB_MYSQL_DRIVER, connection_name); const QString database_name = qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLDatabase)).toString(); database.setHostName(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLHostname)).toString()); database.setPort(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLPort)).toInt()); database.setUserName(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLUsername)).toString()); database.setPassword(TextFactory::decrypt(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLPassword)).toString())); if (!database.open()) { qCritical("MySQL database was NOT opened. Delivered error message: '%s'", qPrintable(database.lastError().text())); // Now, we will display error warning and return SQLite connection. // Also, we set the SQLite driver as active one. qApp->settings()->setValue(GROUP(Database), Database::ActiveDriver, APP_DB_SQLITE_DRIVER); determineDriver(); MessageBox::show(NULL, QMessageBox::Critical, tr("MySQL database not available"), tr("%1 cannot use MySQL storage, it is not available. %1 is now switching to SQLite database. Start your MySQL server " "and make adjustments in application settings.").arg(APP_NAME), QString(), QString()); return connection(objectName(), FromSettings); } else { QSqlQuery query_db(database); query_db.setForwardOnly(true); if (!query_db.exec(QString("USE %1").arg(database_name)) || !query_db.exec(QSL("SELECT inf_value FROM Information WHERE inf_key = 'schema_version'"))) { // If no "rssguard" database exists or schema version is wrong, then initialize it. qWarning("Error occurred. MySQL database is not initialized. Initializing now."); QFile file_init(APP_MISC_PATH + QDir::separator() + APP_DB_MYSQL_INIT); if (!file_init.open(QIODevice::ReadOnly | QIODevice::Text)) { // Database initialization file not opened. HUGE problem. qFatal("MySQL database initialization file '%s' from directory '%s' was not found. File-based database is uninitialized.", APP_DB_MYSQL_INIT, qPrintable(APP_MISC_PATH)); } const QStringList statements = QString(file_init.readAll()).split(APP_DB_COMMENT_SPLIT, QString::SkipEmptyParts); database.transaction(); foreach(QString statement, statements) { // Assign real database name and run the query. query_db.exec(statement.replace(APP_DB_NAME_PLACEHOLDER, database_name)); if (query_db.lastError().isValid()) { qFatal("MySQL database initialization failed. Initialization script '%s' is not correct. Error : '%s'.", APP_DB_MYSQL_INIT, qPrintable(query_db.lastError().databaseText())); } } database.commit(); qDebug("MySQL database backend should be ready now."); } else { // Database was previously initialized. Now just check the schema version. query_db.next(); const QString installed_db_schema = query_db.value(0).toString(); if (installed_db_schema < APP_DB_SCHEMA_VERSION) { if (mysqlUpdateDatabaseSchema(database, installed_db_schema)) { qDebug("Database schema was updated from '%s' to '%s' successully or it is already up to date.", qPrintable(installed_db_schema), APP_DB_SCHEMA_VERSION); } else { qFatal("Database schema was not updated from '%s' to '%s' successully.", qPrintable(installed_db_schema), APP_DB_SCHEMA_VERSION); } } } query_db.finish(); } // Everything is initialized now. m_mysqlDatabaseInitialized = true; return database; } bool DatabaseFactory::mysqlVacuumDatabase() { QSqlDatabase database = mysqlConnection(objectName()); QSqlQuery query_vacuum(database); return query_vacuum.exec(QSL("OPTIMIZE TABLE rssguard.feeds;")) && query_vacuum.exec(QSL("OPTIMIZE TABLE rssguard.messages;")); } QSqlDatabase DatabaseFactory::sqliteConnection(const QString &connection_name, DatabaseFactory::DesiredType desired_type) { if (desired_type == DatabaseFactory::StrictlyInMemory || (desired_type == DatabaseFactory::FromSettings && m_activeDatabaseDriver == SQLITE_MEMORY)) { // We request in-memory database (either user explicitly // needs in-memory database or it was enabled in the settings). if (!m_sqliteInMemoryDatabaseInitialized) { // It is not initialized yet. return sqliteInitializeInMemoryDatabase(); } else { QSqlDatabase database = QSqlDatabase::database(); database.setDatabaseName(QSL(":memory:")); if (!database.isOpen() && !database.open()) { qFatal("In-memory SQLite database was NOT opened. Delivered error message: '%s'.", qPrintable(database.lastError().text())); } else { qDebug("In-memory SQLite database connection seems to be established."); } return database; } } else { // We request file-based database. if (!m_sqliteFileBasedDatabaseinitialized) { // File-based database is not yet initialised. return sqliteInitializeFileBasedDatabase(connection_name); } else { QSqlDatabase database; if (QSqlDatabase::contains(connection_name)) { qDebug("SQLite connection '%s' is already active.", qPrintable(connection_name)); // This database connection was added previously, no need to // setup its properties. database = QSqlDatabase::database(connection_name); } else { // Database connection with this name does not exist // yet, add it and set it up. database = QSqlDatabase::addDatabase(APP_DB_SQLITE_DRIVER, connection_name); const QDir db_path(m_sqliteDatabaseFilePath); QFile db_file(db_path.absoluteFilePath(APP_DB_SQLITE_FILE)); // Setup database file path. database.setDatabaseName(db_file.fileName()); } if (!database.isOpen() && !database.open()) { qFatal("File-based SQLite database was NOT opened. Delivered error message: '%s'.", qPrintable(database.lastError().text())); } else { qDebug("File-based SQLite database connection '%s' to file '%s' seems to be established.", qPrintable(connection_name), qPrintable(QDir::toNativeSeparators(database.databaseName()))); } return database; } } } bool DatabaseFactory::sqliteVacuumDatabase() { QSqlDatabase database; if (m_activeDatabaseDriver == SQLITE) { database = sqliteConnection(objectName(), StrictlyFileBased); } else if (m_activeDatabaseDriver == SQLITE_MEMORY) { sqliteSaveMemoryDatabase(); database = sqliteConnection(objectName(), StrictlyFileBased); } else { return false; } QSqlQuery query_vacuum(database); return query_vacuum.exec(QSL("VACUUM")); } void DatabaseFactory::saveDatabase() { switch (m_activeDatabaseDriver) { case SQLITE_MEMORY: sqliteSaveMemoryDatabase(); break; default: break; } } bool DatabaseFactory::vacuumDatabase() { switch (m_activeDatabaseDriver) { case SQLITE_MEMORY: case SQLITE: return sqliteVacuumDatabase(); case MYSQL: return mysqlVacuumDatabase(); default: return false; } }