save
This commit is contained in:
parent
be07b2d514
commit
654997227b
@ -12,7 +12,9 @@
|
||||
DatabaseDriver::DatabaseDriver(QObject* parent) : QObject(parent)
|
||||
{}
|
||||
|
||||
QStringList DatabaseDriver::prepareScript(const QString& base_sql_folder, const QString& sql_file, const QString& database_name) {
|
||||
QStringList DatabaseDriver::prepareScript(const QString& base_sql_folder,
|
||||
const QString& sql_file,
|
||||
const QString& database_name) {
|
||||
QStringList statements;
|
||||
auto next_file = base_sql_folder + QDir::separator() + sql_file;
|
||||
QString sql_script = QString::fromUtf8(IOFactory::readFile(next_file));
|
||||
@ -30,11 +32,11 @@ QStringList DatabaseDriver::prepareScript(const QString& base_sql_folder, const
|
||||
|
||||
auto included_file = base_sql_folder + QDir::separator() + included_file_name;
|
||||
QString included_sql_script = QString::fromUtf8(IOFactory::readFile(included_file));
|
||||
auto included_statements = sql_script.split(APP_DB_COMMENT_SPLIT,
|
||||
auto included_statements = included_sql_script.split(APP_DB_COMMENT_SPLIT,
|
||||
#if QT_VERSION >= 0x050F00 // Qt >= 5.15.0
|
||||
Qt::SplitBehaviorFlags::SkipEmptyParts);
|
||||
Qt::SplitBehaviorFlags::SkipEmptyParts);
|
||||
#else
|
||||
QString::SplitBehavior::SkipEmptyParts);
|
||||
QString::SplitBehavior::SkipEmptyParts);
|
||||
#endif
|
||||
|
||||
statements << included_statements;
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "database/databasefactory.h"
|
||||
|
||||
#include "3rd-party/boolinq/boolinq.h"
|
||||
#include "database/mariadbdriver.h"
|
||||
#include "database/sqlitedriver.h"
|
||||
#include "gui/messagebox.h"
|
||||
#include "miscellaneous/application.h"
|
||||
@ -19,28 +20,6 @@ DatabaseFactory::DatabaseFactory(QObject* parent)
|
||||
determineDriver();
|
||||
}
|
||||
|
||||
/*else if (m_activeDatabaseDriver == DatabaseDriver::UsedDriver::MYSQL) {
|
||||
QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className(),
|
||||
DatabaseDriver::DesiredType::FromSettings);
|
||||
QSqlQuery query(database);
|
||||
|
||||
query.prepare("SELECT Round(Sum(data_length + index_length), 1) "
|
||||
"FROM information_schema.tables "
|
||||
"WHERE table_schema = :db "
|
||||
"GROUP BY table_schema;");
|
||||
query.bindValue(QSL(":db"), database.databaseName());
|
||||
|
||||
if (query.exec() && query.next()) {
|
||||
return query.value(0).value<qint64>();
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}*/
|
||||
|
||||
void DatabaseFactory::removeConnection(const QString& connection_name) {
|
||||
qDebugNN << LOGSEC_DB << "Removing database connection '" << connection_name << "'.";
|
||||
QSqlDatabase::removeDatabase(connection_name);
|
||||
@ -52,7 +31,7 @@ void DatabaseFactory::determineDriver() {
|
||||
};
|
||||
|
||||
if (QSqlDatabase::isDriverAvailable(APP_DB_MYSQL_DRIVER)) {
|
||||
//m_allDbDrivers.append(new MariaDbDriver(this));
|
||||
m_allDbDrivers.append(new MariaDbDriver(this));
|
||||
}
|
||||
|
||||
const QString db_driver = qApp->settings()->value(GROUP(Database), SETTING(Database::ActiveDriver)).toString();
|
||||
|
@ -13,19 +13,6 @@ class DatabaseFactory : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
// Describes possible MySQL-specific errors.
|
||||
enum class MySQLError {
|
||||
Ok = 0,
|
||||
UnknownError = 1,
|
||||
AccessDenied = 1045,
|
||||
UnknownDatabase = 1049,
|
||||
ConnectionError = 2002,
|
||||
CantConnect = 2003,
|
||||
UnknownHost = 2005
|
||||
};
|
||||
|
||||
// Constructor.
|
||||
explicit DatabaseFactory(QObject* parent = nullptr);
|
||||
|
||||
// Removes connection.
|
||||
|
298
src/librssguard/database/mariadbdriver.cpp
Executable file
298
src/librssguard/database/mariadbdriver.cpp
Executable file
@ -0,0 +1,298 @@
|
||||
// For license of this file, see <project-root-folder>/LICENSE.md.
|
||||
|
||||
#include "database/mariadbdriver.h"
|
||||
|
||||
#include "definitions/definitions.h"
|
||||
#include "exceptions/applicationexception.h"
|
||||
#include "miscellaneous/application.h"
|
||||
#include "miscellaneous/settings.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QSqlError>
|
||||
#include <QSqlQuery>
|
||||
|
||||
MariaDbDriver::MariaDbDriver(QObject* parent) : DatabaseDriver(parent), m_databaseInitialized(false) {}
|
||||
|
||||
MariaDbDriver::MariaDbError MariaDbDriver::testConnection(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()) {
|
||||
qDebugNN << LOGSEC_DB
|
||||
<< "Checked MySQL database, version is"
|
||||
<< QUOTE_W_SPACE_DOT(query.value(0).toString());
|
||||
|
||||
// Connection succeeded, clean up the mess and return OK status.
|
||||
database.close();
|
||||
return MariaDbError::Ok;
|
||||
}
|
||||
else {
|
||||
database.close();
|
||||
return MariaDbError::UnknownError;
|
||||
}
|
||||
}
|
||||
else if (database.lastError().isValid()) {
|
||||
auto nat = database.lastError().nativeErrorCode();
|
||||
bool nat_converted = false;
|
||||
auto nat_int = nat.toInt(&nat_converted);
|
||||
|
||||
if (nat_converted) {
|
||||
return static_cast<MariaDbError>(nat_int);
|
||||
}
|
||||
else {
|
||||
qWarningNN << LOGSEC_DB
|
||||
<< "Failed to recognize MySQL error code:"
|
||||
<< QUOTE_W_SPACE_DOT(nat);
|
||||
|
||||
return MariaDbError::UnknownError;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return MariaDbError::UnknownError;
|
||||
}
|
||||
}
|
||||
|
||||
QString MariaDbDriver::interpretErrorCode(MariaDbDriver::MariaDbError error_code) const {
|
||||
switch (error_code) {
|
||||
case MariaDbError::Ok:
|
||||
return tr("MySQL server works as expected.");
|
||||
|
||||
case MariaDbError::UnknownDatabase:
|
||||
return tr("Selected database does not exist (yet). It will be created. It's okay.");
|
||||
|
||||
case MariaDbError::CantConnect:
|
||||
case MariaDbError::ConnectionError:
|
||||
case MariaDbError::UnknownHost:
|
||||
return tr("No MySQL server is running in the target destination.");
|
||||
|
||||
case MariaDbError::AccessDenied:
|
||||
return tr("Access denied. Invalid username or password used.");
|
||||
|
||||
default:
|
||||
return tr("Unknown error: '%1'.").arg(int(error_code));
|
||||
}
|
||||
}
|
||||
|
||||
QString MariaDbDriver::humanDriverType() const {
|
||||
return tr("MariaDB");
|
||||
}
|
||||
|
||||
QString MariaDbDriver::qtDriverCode() const {
|
||||
return APP_DB_MYSQL_DRIVER;
|
||||
}
|
||||
|
||||
DatabaseDriver::DriverType MariaDbDriver::driverType() const {
|
||||
return DatabaseDriver::DriverType::MySQL;
|
||||
}
|
||||
|
||||
bool MariaDbDriver::vacuumDatabase() {
|
||||
QSqlDatabase database = connection(objectName());
|
||||
QSqlQuery query_vacuum(database);
|
||||
|
||||
return query_vacuum.exec(QSL("OPTIMIZE TABLE Feeds;")) &&
|
||||
query_vacuum.exec(QSL("OPTIMIZE TABLE Messages;"));
|
||||
}
|
||||
|
||||
bool MariaDbDriver::saveDatabase() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void MariaDbDriver::backupDatabase(const QString& backup_folder, const QString& backup_name) {
|
||||
Q_UNUSED(backup_folder)
|
||||
Q_UNUSED(backup_name)
|
||||
}
|
||||
|
||||
bool MariaDbDriver::initiateRestoration(const QString& database_package_file) {
|
||||
Q_UNUSED(database_package_file)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MariaDbDriver::finishRestoration() {
|
||||
return true;
|
||||
}
|
||||
|
||||
qint64 MariaDbDriver::databaseDataSize() {
|
||||
QSqlDatabase database = connection(metaObject()->className());
|
||||
QSqlQuery query(database);
|
||||
|
||||
query.prepare("SELECT Round(Sum(data_length + index_length), 1) "
|
||||
"FROM information_schema.tables "
|
||||
"WHERE table_schema = :db "
|
||||
"GROUP BY table_schema;");
|
||||
query.bindValue(QSL(":db"), database.databaseName());
|
||||
|
||||
if (query.exec() && query.next()) {
|
||||
return query.value(0).value<qint64>();
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
QSqlDatabase MariaDbDriver::initializeDatabase(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(qApp->settings()->password(GROUP(Database), SETTING(Database::MySQLPassword)).toString());
|
||||
|
||||
if (!database.open()) {
|
||||
qFatal("Cannot open MySQL database: %s.", qPrintable(database.lastError().text()));
|
||||
}
|
||||
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.
|
||||
qWarningNN << LOGSEC_DB << "Error occurred. MySQL database is not initialized. Initializing now.";
|
||||
|
||||
try {
|
||||
const QStringList statements = prepareScript(APP_SQL_PATH, APP_DB_MYSQL_INIT, database_name);
|
||||
|
||||
for (const QString& statement : statements) {
|
||||
query_db.exec(statement);
|
||||
|
||||
if (query_db.lastError().isValid()) {
|
||||
throw ApplicationException(query_db.lastError().text());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const ApplicationException& ex) {
|
||||
qFatal("Error when running SQL scripts: %s.", qPrintable(ex.message()));
|
||||
}
|
||||
|
||||
qDebugNN << LOGSEC_DB << "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.toInt() < QString(APP_DB_SCHEMA_VERSION).toInt()) {
|
||||
if (updateDatabaseSchema(database, installed_db_schema, database_name)) {
|
||||
qDebugNN << LOGSEC_DB
|
||||
<< "Database schema was updated from '"
|
||||
<< installed_db_schema
|
||||
<< "' to '"
|
||||
<< APP_DB_SCHEMA_VERSION
|
||||
<< "' successully or it is already up to date.";
|
||||
}
|
||||
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_databaseInitialized = true;
|
||||
return database;
|
||||
}
|
||||
|
||||
bool MariaDbDriver::updateDatabaseSchema(const QSqlDatabase& database,
|
||||
const QString& source_db_schema_version,
|
||||
const QString& database_name) {
|
||||
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) {
|
||||
try {
|
||||
const QStringList statements = prepareScript(APP_SQL_PATH,
|
||||
QString(APP_DB_UPDATE_FILE_PATTERN).arg(QSL("mysql"),
|
||||
QString::number(working_version),
|
||||
QString::number(working_version + 1)),
|
||||
database_name);
|
||||
|
||||
for (const QString& statement : statements) {
|
||||
QSqlQuery query = database.exec(statement);
|
||||
|
||||
if (!query.exec(statement) && query.lastError().isValid()) {
|
||||
throw ApplicationException(query.lastError().text());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const ApplicationException& ex) {
|
||||
qFatal("Error when running SQL scripts: %s.", qPrintable(ex.message()));
|
||||
}
|
||||
|
||||
// Increment the version.
|
||||
qDebugNN << LOGSEC_DB
|
||||
<< "Updating database schema: '"
|
||||
<< working_version
|
||||
<< "' -> '"
|
||||
<< working_version + 1
|
||||
<< "'.";
|
||||
|
||||
working_version++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QSqlDatabase MariaDbDriver::connection(const QString& connection_name, DatabaseDriver::DesiredStorageType desired_type) {
|
||||
Q_UNUSED(desired_type)
|
||||
|
||||
if (!m_databaseInitialized) {
|
||||
// Return initialized database.
|
||||
return initializeDatabase(connection_name);
|
||||
}
|
||||
else {
|
||||
QSqlDatabase database;
|
||||
|
||||
if (QSqlDatabase::contains(connection_name)) {
|
||||
qDebugNN << LOGSEC_DB
|
||||
<< "MySQL connection '"
|
||||
<< connection_name
|
||||
<< "' is already active.";
|
||||
|
||||
// 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(qApp->settings()->password(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 {
|
||||
qDebugNN << LOGSEC_DB
|
||||
<< "MySQL database connection"
|
||||
<< QUOTE_W_SPACE(connection_name)
|
||||
<< "to file"
|
||||
<< QUOTE_W_SPACE(QDir::toNativeSeparators(database.databaseName()))
|
||||
<< "seems to be established.";
|
||||
}
|
||||
|
||||
return database;
|
||||
}
|
||||
}
|
49
src/librssguard/database/mariadbdriver.h
Executable file
49
src/librssguard/database/mariadbdriver.h
Executable file
@ -0,0 +1,49 @@
|
||||
// For license of this file, see <project-root-folder>/LICENSE.md.
|
||||
|
||||
#ifndef MARIADBDRIVER_H
|
||||
#define MARIADBDRIVER_H
|
||||
|
||||
#include "database/databasedriver.h"
|
||||
|
||||
class MariaDbDriver : public DatabaseDriver {
|
||||
public:
|
||||
enum class MariaDbError {
|
||||
Ok = 0,
|
||||
UnknownError = 1,
|
||||
AccessDenied = 1045,
|
||||
UnknownDatabase = 1049,
|
||||
ConnectionError = 2002,
|
||||
CantConnect = 2003,
|
||||
UnknownHost = 2005
|
||||
};
|
||||
|
||||
explicit MariaDbDriver(QObject* parent = nullptr);
|
||||
|
||||
MariaDbError testConnection(const QString& hostname, int port, const QString& w_database,
|
||||
const QString& username, const QString& password);
|
||||
|
||||
virtual QString humanDriverType() const;
|
||||
virtual QString qtDriverCode() const;
|
||||
virtual DriverType driverType() const;
|
||||
virtual bool vacuumDatabase();
|
||||
virtual bool saveDatabase();
|
||||
virtual void backupDatabase(const QString& backup_folder, const QString& backup_name);
|
||||
virtual bool initiateRestoration(const QString& database_package_file);
|
||||
virtual bool finishRestoration();
|
||||
virtual qint64 databaseDataSize();
|
||||
virtual QSqlDatabase connection(const QString& connection_name,
|
||||
DatabaseDriver::DesiredStorageType desired_type = DatabaseDriver::DesiredStorageType::FromSettings);
|
||||
|
||||
QString interpretErrorCode(MariaDbError error_code) const;
|
||||
|
||||
private:
|
||||
bool updateDatabaseSchema(const QSqlDatabase& database,
|
||||
const QString& source_db_schema_version,
|
||||
const QString& database_name);
|
||||
QSqlDatabase initializeDatabase(const QString& connection_name);
|
||||
|
||||
private:
|
||||
bool m_databaseInitialized;
|
||||
};
|
||||
|
||||
#endif // MARIADBDRIVER_H
|
@ -3,6 +3,7 @@
|
||||
#include "gui/settings/settingsdatabase.h"
|
||||
|
||||
#include "database/databasefactory.h"
|
||||
#include "database/mariadbdriver.h"
|
||||
#include "definitions/definitions.h"
|
||||
#include "gui/guiutilities.h"
|
||||
#include "miscellaneous/application.h"
|
||||
@ -44,27 +45,24 @@ SettingsDatabase::~SettingsDatabase() {
|
||||
}
|
||||
|
||||
void SettingsDatabase::mysqlTestConnection() {
|
||||
// TODO: TODO
|
||||
MariaDbDriver* driv = static_cast<MariaDbDriver*>(qApp->database()->driver());
|
||||
const MariaDbDriver::MariaDbError error_code = driv->testConnection(m_ui->m_txtMysqlHostname->lineEdit()->text(),
|
||||
m_ui->m_spinMysqlPort->value(),
|
||||
m_ui->m_txtMysqlDatabase->lineEdit()->text(),
|
||||
m_ui->m_txtMysqlUsername->lineEdit()->text(),
|
||||
m_ui->m_txtMysqlPassword->lineEdit()->text());
|
||||
const QString interpretation = driv->interpretErrorCode(error_code);
|
||||
|
||||
/*
|
||||
const DatabaseFactory::MySQLError error_code = qApp->database()->driver()->mysqlTestConnection(m_ui->m_txtMysqlHostname->lineEdit()->text(),
|
||||
m_ui->m_spinMysqlPort->value(),
|
||||
m_ui->m_txtMysqlDatabase->lineEdit()->text(),
|
||||
m_ui->m_txtMysqlUsername->lineEdit()->text(),
|
||||
m_ui->m_txtMysqlPassword->lineEdit()->text());
|
||||
const QString interpretation = qApp->database()->driver()->mysqlInterpretErrorCode(error_code);
|
||||
|
||||
switch (error_code) {
|
||||
case DatabaseFactory::MySQLError::Ok:
|
||||
case DatabaseFactory::MySQLError::UnknownDatabase:
|
||||
switch (error_code) {
|
||||
case MariaDbDriver::MariaDbError::Ok:
|
||||
case MariaDbDriver::MariaDbError::UnknownDatabase:
|
||||
m_ui->m_lblMysqlTestResult->setStatus(WidgetWithStatus::StatusType::Ok, interpretation, interpretation);
|
||||
break;
|
||||
|
||||
default:
|
||||
default:
|
||||
m_ui->m_lblMysqlTestResult->setStatus(WidgetWithStatus::StatusType::Error, interpretation, interpretation);
|
||||
break;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsDatabase::onMysqlHostnameChanged(const QString& new_hostname) {
|
||||
|
@ -47,6 +47,7 @@ HEADERS += core/feeddownloader.h \
|
||||
database/databasedriver.h \
|
||||
database/databasefactory.h \
|
||||
database/databasequeries.h \
|
||||
database/mariadbdriver.h \
|
||||
database/sqlitedriver.h \
|
||||
definitions/definitions.h \
|
||||
definitions/typedefs.h \
|
||||
@ -228,6 +229,7 @@ SOURCES += core/feeddownloader.cpp \
|
||||
database/databasedriver.cpp \
|
||||
database/databasefactory.cpp \
|
||||
database/databasequeries.cpp \
|
||||
database/mariadbdriver.cpp \
|
||||
database/sqlitedriver.cpp \
|
||||
dynamic-shortcuts/dynamicshortcuts.cpp \
|
||||
dynamic-shortcuts/dynamicshortcutswidget.cpp \
|
||||
|
Loading…
x
Reference in New Issue
Block a user