forked from ondra/colnod-connector
318 lines
11 KiB
C++
318 lines
11 KiB
C++
#include "databaseapi.h"
|
|
|
|
Q_LOGGING_CATEGORY(database, "database.api");
|
|
|
|
|
|
|
|
DatabaseAPI::DatabaseAPI(serversdk::DatabaseManager *dbMan, bool dryMode)
|
|
: mDryMode(dryMode),
|
|
mDbMan(dbMan),
|
|
utils(std::make_unique<serversdk::DatabaseUtils>(dbMan))
|
|
{
|
|
mDbMan->setDryMode(dryMode);
|
|
}
|
|
|
|
bool DatabaseAPI::open(const QString& host, const QString& name, const QString& user, const QString& password)
|
|
{
|
|
return mDbMan->openPostgres(host, name, user, password);
|
|
}
|
|
|
|
bool DatabaseAPI::executeQuery(QSqlQuery &query, const QString &statement)
|
|
{
|
|
if (statement.isEmpty()) {
|
|
query = mDbMan->executeQuery(query);
|
|
}
|
|
else {
|
|
query = mDbMan->executeQuery(query, statement);
|
|
}
|
|
if (!query.isActive()) {
|
|
this->mValid = false;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::shared_ptr<Data> DatabaseAPI::downloadData()
|
|
{
|
|
auto updatedQuery = mDbMan->createQuery();
|
|
auto deletedQuery = mDbMan->createQuery();
|
|
if (!executeQuery(updatedQuery, "SELECT * FROM colnod_token WHERE last_modified > last_update AND deleted = false")) {
|
|
qWarning(database) << "ERROR during geting new/updated tokens from db.";
|
|
return nullptr;
|
|
}
|
|
if (!executeQuery(deletedQuery, "SELECT id FROM colnod_token WHERE last_modified > last_update AND deleted = true")) {
|
|
qWarning(database) << "ERROR during geting marked tokens for deletion from db.";
|
|
return nullptr;
|
|
}
|
|
|
|
return composeData(updatedQuery, deletedQuery);
|
|
}
|
|
|
|
bool DatabaseAPI::processData(const std::shared_ptr<Data> &data)
|
|
{
|
|
bool res = true;
|
|
if (!uploadTokens(data->updatedTokens)) {
|
|
res = false;
|
|
}
|
|
if (!uploadSubjects(data->updatedSubjects)) {
|
|
res = false;
|
|
}
|
|
if (!deleteItems(data->deletedTokens, "colnod_token")) {
|
|
res = false;
|
|
}
|
|
if (!deleteItems(data->deletedSubjects, "colnod_subject")) {
|
|
res = false;
|
|
}
|
|
|
|
qInfo(database) << tab << "- push db:" << tab << "tokens [" << data->updatedTokens.size() << ":" << data->deletedTokens.size() <<
|
|
"] subjects [" << data->updatedSubjects.size() << ":" << data->deletedSubjects.size() << "] [ added/updated : deleted ]";
|
|
|
|
if (!data->updatedTokens.isEmpty() || !data->deletedTokens.isEmpty() ||
|
|
!data->updatedSubjects.isEmpty() || !data->deletedSubjects.isEmpty()) {
|
|
qInfo(database) << dash;
|
|
}
|
|
for (const auto &token : data->updatedTokens) {
|
|
qInfo(database) << Message::massageTemplate("Updated", "token", "database", token->name());
|
|
}
|
|
for (const auto &id : data->deletedTokens) {
|
|
qInfo(database) << Message::massageTemplate("Deleted", "token", "database", id);
|
|
}
|
|
for (const auto &subject : data->updatedSubjects) {
|
|
qInfo(database) << Message::massageTemplate("Updated", "subject", "database", subject->name());
|
|
}
|
|
for (const auto &id : data->deletedSubjects) {
|
|
qInfo(database) << Message::massageTemplate("Deleted", "subject", "database", id);
|
|
}
|
|
if (!data->updatedTokens.isEmpty() || !data->deletedTokens.isEmpty() ||
|
|
!data->updatedSubjects.isEmpty() || !data->deletedSubjects.isEmpty()) {
|
|
qInfo(database) << dash;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
// save data to structure Data to be sent to syncManager
|
|
std::shared_ptr<Data> DatabaseAPI::composeData(const QSqlQuery &updatedQuery, QSqlQuery &deletedQuery)
|
|
{
|
|
auto data = std::make_shared<Data>();
|
|
auto list = utils->tableFromQuery<Token>(updatedQuery);
|
|
for (const auto &token : list) {
|
|
data->updatedTokens.insert(token->id(), token);
|
|
}
|
|
while (deletedQuery.next()) {
|
|
data->deletedTokens.append(deletedQuery.value(0).toString());
|
|
}
|
|
|
|
qInfo(database) << tab << "- src db:" << tab << "tokens [" << data->updatedTokens.size() << ":" << data->deletedTokens.size() <<
|
|
"] [ added/updated : deleted ]";
|
|
|
|
if (!data->updatedTokens.isEmpty() || !data->deletedTokens.isEmpty()) {
|
|
qInfo(database) << dash;
|
|
}
|
|
for (const auto &token : data->updatedTokens) {
|
|
qInfo(database) << Message::massageTemplate("Download updated", "token", "database", token->name());
|
|
}
|
|
for (const auto &id : data->deletedTokens) {
|
|
qInfo(database) << Message::massageTemplate("Download deleted", "token", "database", id);
|
|
}
|
|
if (!data->updatedTokens.isEmpty() || !data->deletedTokens.isEmpty()) {
|
|
qInfo(database) << dash;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
// Time of last synchronization
|
|
qint64 DatabaseAPI::getLastSync()
|
|
{
|
|
auto query = mDbMan->createQuery();
|
|
if (!executeQuery(query, "SELECT last_sync FROM config")) {
|
|
qCritical(database) << "ERROR during getting time of last sync. Exit.";
|
|
return -1;
|
|
}
|
|
if (query.next()){
|
|
return query.value(0).toLongLong();
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
bool DatabaseAPI::setLastSync(qint64 timestamp)
|
|
{
|
|
auto query = mDbMan->createQuery();
|
|
query.prepare("UPDATE config SET last_sync = :last_sync");
|
|
query.bindValue(":last_sync", timestamp);
|
|
if (!executeQuery(query)) {
|
|
qCritical(database) << "ERROR during set time of this synchronization as last sync time";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// It update last_update time in db of deleted tokens, because in colnod is later time (time of real deletion from Colnod)
|
|
void DatabaseAPI::reupdateDeleted(const QList<QString>& ids)
|
|
{
|
|
auto query = mDbMan->createQuery();
|
|
for (const auto &id : ids){
|
|
query.prepare("UPDATE colnod_token SET last_update = :last_update WHERE id = :id");
|
|
query.bindValue(":last_update", QDateTime::fromMSecsSinceEpoch(getNow()).toString("yyyy-MM-dd hh:mm:ss.zzz"));
|
|
query.bindValue(":id", id);
|
|
if (!executeQuery(query)) {
|
|
if (!executeQuery(query)) {
|
|
qWarning(database) << "ERROR during updating last update of deleted token:" << id << ", it will be synced from Colnod to db in next sync. Last modified will be invalid";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
qint64 DatabaseAPI::getNow()
|
|
{
|
|
auto query = mDbMan->createQuery();
|
|
if (!executeQuery(query, "SELECT EXTRACT(EPOCH FROM NOW())*1000")) {
|
|
qCritical(database) << "ERROR during getting acctual time from db";
|
|
return -1;
|
|
}
|
|
query.next();
|
|
return query.value(0).toLongLong();
|
|
}
|
|
|
|
// All tokens which are pushed to Colnad are update back to db, because of diffrent last_update and init of new tokens
|
|
bool DatabaseAPI::uploadTokens(const QHash<QString, std::shared_ptr<Token>> &tokens, const QSet<QString> &filters)
|
|
{
|
|
auto query = mDbMan->createQuery();
|
|
for (const auto &token : tokens) {
|
|
if (exists(token->id(), "colnod_token")) {
|
|
query = utils->updateTable(token.get(), "colnod_token", filters);
|
|
}
|
|
else {
|
|
query = utils->insertIntoTable(token.get(), "colnod_token");
|
|
}
|
|
if (!query.isActive()) {
|
|
mValid = false;
|
|
qWarning(database) << "ERROR durning inserting/updating of token:" << token->name();
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool DatabaseAPI::uploadSubjects(const QHash<QString, std::shared_ptr<Subject>> &subjects)
|
|
{
|
|
auto query = mDbMan->createQuery();
|
|
for (const auto &subject : subjects) {
|
|
for (const auto &attribute : subject->getAttributes()) {
|
|
|
|
if (exists(attribute->id(), "colnod_subject_attribute")) {
|
|
query = utils->updateTable(attribute.get(), "colnod_subject_attribute");
|
|
}
|
|
else {
|
|
query = utils->insertIntoTable(attribute.get(), "colnod_subject_attribute");
|
|
}
|
|
if (!query.isActive()) {
|
|
mValid = false;
|
|
qWarning(database) << "ERROR durning inserting/updating of subject attribute:" << attribute->attributeValue();
|
|
}
|
|
}
|
|
if (exists(subject->id(), "colnod_subject")) {
|
|
query = utils->updateTable(subject.get(), "colnod_subject");
|
|
}
|
|
else {
|
|
query = utils->insertIntoTable(subject.get(), "colnod_subject");
|
|
}
|
|
if (!query.isActive()) {
|
|
mValid = false;
|
|
qWarning(database) << "ERROR durning inserting/updating of subject attribute:" << subject->name();
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool DatabaseAPI::deleteItems(const QList<QString> &ids, const QString &tableName)
|
|
{
|
|
auto query = mDbMan->createQuery();
|
|
QString nowString = QDateTime::fromMSecsSinceEpoch(getLastSync()).toString("yyyy-MM-dd hh:mm:ss.zzz");
|
|
|
|
if (tableName == "colnod_subject") {
|
|
for (const auto &id : ids) {
|
|
query.prepare("DELETE FROM colnod_subject_attribute WHERE subject_id = :id");
|
|
query.bindValue(":id", id);
|
|
if (!executeQuery(query)) {
|
|
qWarning(database) << "ERROR during marking as deleted:" << id;
|
|
mValid = false;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const auto &id : ids) {
|
|
query.prepare(QString("UPDATE %1 SET deleted = true, last_update = :now, last_modified = :now WHERE id = :id").arg(tableName));
|
|
query.bindValue(":id", id);
|
|
query.bindValue(":now", nowString);
|
|
if (!executeQuery(query)) {
|
|
qWarning(database) << "ERROR during marking as deleted:" << id;
|
|
mValid = false;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::shared_ptr<Token> DatabaseAPI::getToken(const QString &id)
|
|
{
|
|
auto query = mDbMan->createQuery();
|
|
query.prepare("SELECT * FROM colnod_token WHERE id = :id");
|
|
query.bindValue(":id", id);
|
|
if (!executeQuery(query)) {
|
|
qCritical(database) << "ERROR during accessing existing tokens. EXIT";
|
|
return nullptr;
|
|
}
|
|
auto list = utils->tableFromQuery<Token>(query);
|
|
if (list.isEmpty())
|
|
return std::make_shared<Token>();
|
|
return list.at(0);
|
|
}
|
|
|
|
bool DatabaseAPI::isValid()
|
|
{
|
|
return mValid;
|
|
}
|
|
|
|
int DatabaseAPI::exists(const QString &id, const QString &tableName)
|
|
{
|
|
auto query = mDbMan->createQuery();
|
|
query.prepare(QString("SELECT 1 FROM %1 WHERE id = :id").arg(tableName));
|
|
query.bindValue(":id", id);
|
|
if (!executeQuery(query)) {
|
|
qCritical(database) << "ERROR during accessing existing tokens. EXIT(1)";
|
|
return -1;
|
|
}
|
|
return query.next();
|
|
}
|
|
|
|
qint16 DatabaseAPI::getNumOfTokensOfSubject(const QString &id)
|
|
{
|
|
auto query = mDbMan->createQuery();
|
|
query.prepare("SELECT 1 FROM colnod_token WHERE subject_id = :id AND description = 'mobile'");
|
|
query.bindValue(":id", id);
|
|
if (!executeQuery(query)) {
|
|
qCritical(database) << "ERROR during accessing existing tokens. EXIT(1)";
|
|
return -1;
|
|
}
|
|
return static_cast<qint16>(query.size());
|
|
}
|
|
|
|
QString DatabaseAPI::getSubjectNameByTokenId(const QString &tokenId)
|
|
{
|
|
auto query = mDbMan->createQuery();
|
|
query.prepare("SELECT name FROM colnod_subject WHERE id = (SELECT subject_id FROM colnod_token WHERE id = :tokenId)");
|
|
query.bindValue(":tokenId", tokenId);
|
|
if (!executeQuery(query)) {
|
|
qCritical(database) << "ERROR during accessing existing subject. EXIT(1)";
|
|
return "error";
|
|
}
|
|
if (query.next())
|
|
return query.value(0).toString();
|
|
qWarning() << "Token:" << tokenId << "is not assigned to any subject";
|
|
return "(No subject)";
|
|
}
|