Heart/src/public/database.cpp
2023-02-26 20:07:35 +01:00

276 lines
6.7 KiB
C++

/*
* Copyright (C) 2018-2023 QuasarApp.
* Distributed under the lgplv3 software license, see the accompanying
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
#include "database.h"
#include "sqldbwriter.h"
#include "asyncsqldbwriter.h"
#include <quasarapp.h>
#include <QCoreApplication>
#include <deleteobject.h>
#include <QSet>
#include <itoken.h>
#include <sqlitedbcache.h>
#include <sqldb.h>
#include "getsinglevalue.h"
#include "setsinglevalue.h"
#include <qaglobalutils.h>
namespace QH {
using namespace PKG;
DataBase::DataBase(QObject *ptr): QObject(ptr) {
qRegisterMetaType<QSharedPointer<QH::PKG::DBObject>>();
}
bool DataBase::initSqlDb(QString DBparamsFile,
ISqlDB *cache,
SqlDBWriter *writer) {
initDefaultDbObjects(cache, writer);
QVariantMap params;
_db->setSQLSources(SQLSources());
if (DBparamsFile.isEmpty()) {
params = defaultDbParams();
if (!_db->init(params)) {
return false;
}
} else {
if (!_db->init(DBparamsFile)) {
return false;
}
}
if (!upgradeDataBase()) {
QuasarAppUtils::Params::log("Failed to upgrade database",
QuasarAppUtils::Error);
return false;
}
QuasarAppUtils::Params::log(QString("Database loaded from: %0").arg(dbLocation()),
QuasarAppUtils::Debug);
connect(_db, &ISqlDB::sigItemChanged,
this, &DataBase::sigObjectChanged,
Qt::DirectConnection);
connect(_db, &ISqlDB::sigItemDeleted,
this, &DataBase::sigObjectDeleted,
Qt::DirectConnection);
return true;
}
bool DataBase::isSqlInited() const {
return _db;
}
bool DataBase::run() {
if (!isSqlInited() && !initSqlDb()) {
return false;
}
return true;
}
bool DataBase::run(const QString &localNodeName) {
if (localNodeName.isEmpty())
return false;
setLocalNodeName(localNodeName);
if (!isSqlInited() && !initSqlDb()) {
return false;
}
return true;
}
void DataBase::stop() {
if (db()) {
auto writer = _db->writer();
_db->softDelete();
_db = nullptr;
delete writer;
}
}
DataBase::~DataBase() {
stop();
}
void DataBase::initDefaultDbObjects(ISqlDB *cache,
SqlDBWriter *writer) {
if (!writer) {
writer = new AsyncSqlDBWriter();
}
if (!cache) {
cache = new SqlDB();
}
cache->setWriter(writer);
_db = cache;
}
bool DataBase::welcomeAddress(AbstractNodeInfo *) {
return true;
}
QStringList DataBase::SQLSources() const{
return {
DEFAULT_DB_INIT_FILE_PATH
};
}
QSet<QString> DataBase::systemTables() const {
return {"NetworkMembers", "MemberPermisions"};
}
void DataBase::objectRemoved(const DbAddress &) {
}
void DataBase::objectChanged(const QSharedPointer<DBObject> &) {
}
const DBPatchMap DataBase::dbPatches() const {
return _dbPatches;
}
void DataBase::addDBPatch(const DBPatch &patch) {
debug_assert(patch.isValid(),
"Failed to initialise a Data base patch!"
" Patch object is invalid");
debug_assert(!_dbPatches[patch.versionFrom].contains(patch.versionTo),
"Failed to initialise a Data base patch!");
_dbPatches[patch.versionFrom][patch.versionTo] = patch;
_targetDBVersion = std::max(_targetDBVersion, patch.versionTo);
}
QString DataBase::dbLocation() const {
if (db() && db()->writer()) {
return db()->writer()->databaseLocation();
}
return "";
}
ISqlDB *DataBase::db() const {
return _db;
}
bool DataBase::isForbidenTable(const QString &table) {
return systemTables().contains(table);
}
bool DataBase::upgradeDataBase() {
if (!db())
return false;
DBPatchMap patchesPack = dbPatches();
if (!patchesPack.size()) {
return true;
}
int currentVersion = 0;
bool fsupportUpgrade = db()->doQuery("SELECT COUNT(*) FROM DataBaseAttributes", {}, true);
if (!fsupportUpgrade) {
QuasarAppUtils::Params::log("The data base of application do not support soft upgrade. "
"Please remove database monyaly and restart application."
"You can disable upgrade functions for this override the upgradeDataBase method. ",
QuasarAppUtils::Error);
return false;
}
PKG::GetSingleValue request({"DataBaseAttributes", "version"}, "value", "name");
if (auto responce = _db->getObject(request)) {
currentVersion = responce->value().toInt();
}
if (currentVersion < _targetDBVersion)
onBeforeDBUpgrade(currentVersion, _targetDBVersion);
while (currentVersion < _targetDBVersion) {
QString message;
message = "Upgrade data base from %0 to %1 versions. %2";
message = message.arg(currentVersion);
auto patches = patchesPack.value(currentVersion, {});
if (!patches.size()) {
QuasarAppUtils::Params::log("Failed to " + message.arg("Unknown", "Required patch not found!"),
QuasarAppUtils::Error);
return false;
}
auto patch = patches.last();
message = message.arg(patch.versionTo);
QuasarAppUtils::Params::log(message.arg("(Begin)"),
QuasarAppUtils::Info);
if (!patch.action(db())) {
QuasarAppUtils::Params::log("Failed to " + message.arg("Patch finished with error code!"),
QuasarAppUtils::Error);
return false;
}
currentVersion = patch.versionTo;
auto updateVersionRequest = QSharedPointer<PKG::SetSingleValue>::create(
DbAddress{"DataBaseAttributes", "version"},
"value", currentVersion, "name");
if (!_db->replaceObject(updateVersionRequest, true)) {
QuasarAppUtils::Params::log("Failed to update version attribute",
QuasarAppUtils::Error);
return false;
}
}
return true;
}
void DataBase::onBeforeDBUpgrade(int , int ) const { }
const QString &DataBase::localNodeName() const {
return _localNodeName;
}
void DataBase::setLocalNodeName(const QString &newLocalNodeName) {
_localNodeName = newLocalNodeName;
}
QVariantMap DataBase::defaultDbParams() const {
return {
{"DBDriver", "QSQLITE"},
{"DBFilePath", DEFAULT_DB_PATH + "/" + localNodeName() + "/" + localNodeName() + "_" + DEFAULT_DB_NAME},
};
}
}