X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fcore%2Fpostgresqlstorage.cpp;h=90b0fdbbc4ef8fdbac8869e2073e19e458e902d2;hp=200fd14ca5bde5087fea0357fcd5122849bdb9ac;hb=b134e777b822b929a78455fd92146bf7443e9aa1;hpb=c1cf157116de7fc3da96203aa6f03c38c7ebb650 diff --git a/src/core/postgresqlstorage.cpp b/src/core/postgresqlstorage.cpp index 200fd14c..90b0fdbb 100644 --- a/src/core/postgresqlstorage.cpp +++ b/src/core/postgresqlstorage.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005-2018 by the Quassel Project * + * Copyright (C) 2005-2020 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@ -20,9 +20,11 @@ #include "postgresqlstorage.h" -#include +#include +#include +#include +#include -#include "logmessage.h" #include "network.h" #include "quassel.h" @@ -46,7 +48,7 @@ std::unique_ptr PostgreSqlStorage::createMigrationWr bool PostgreSqlStorage::isAvailable() const { if (!QSqlDatabase::isDriverAvailable("QPSQL")) { - quWarning() << qPrintable(tr("PostgreSQL driver plugin not available for Qt. Installed drivers:")) + qWarning() << qPrintable(tr("PostgreSQL driver plugin not available for Qt. Installed drivers:")) << qPrintable(QSqlDatabase::drivers().join(", ")); return false; } @@ -92,7 +94,7 @@ bool PostgreSqlStorage::initDbSession(QSqlDatabase& db) // as this is the expected behavior. // If it is a newer version, switch to legacy mode. - quWarning() << "Switching Postgres to legacy mode. (set standard conforming strings to off)"; + qWarning() << "Switching Postgres to legacy mode. (set standard conforming strings to off)"; // If the following calls fail, it is a legacy DB anyways, so it doesn't matter // and no need to check the outcome. db.exec("set standard_conforming_strings = off"); @@ -106,14 +108,14 @@ bool PostgreSqlStorage::initDbSession(QSqlDatabase& db) if (query.lastError().isValid()) { // We cannot enable standard conforming strings... // since Quassel does no escaping by itself, this would yield a major vulnerability. - quError() << "Failed to enable standard_conforming_strings for the Postgres db!"; + qCritical() << "Failed to enable standard_conforming_strings for the Postgres db!"; return false; } } break; default: // The slash got replaced with 0 or more than 2 slashes! o_O - quError() << "Your version of Qt does something _VERY_ strange to slashes in QSqlQueries! You should consult your trusted doctor!"; + qCritical() << "Your version of Qt does something _VERY_ strange to slashes in QSqlQueries! You should consult your trusted doctor!"; return false; break; } @@ -121,7 +123,7 @@ bool PostgreSqlStorage::initDbSession(QSqlDatabase& db) // Set the PostgreSQL session timezone to UTC, since we want timestamps stored in UTC QSqlQuery tzQuery = db.exec("SET timezone = 'UTC'"); if (tzQuery.lastError().isValid()) { - quError() << "Failed to set timezone to UTC!"; + qCritical() << "Failed to set timezone to UTC!"; return false; } @@ -165,19 +167,39 @@ int PostgreSqlStorage::installedSchemaVersion() return AbstractSqlStorage::installedSchemaVersion(); } -bool PostgreSqlStorage::updateSchemaVersion(int newVersion) +bool PostgreSqlStorage::updateSchemaVersion(int newVersion, bool clearUpgradeStep) { - QSqlQuery query(logDb()); + // Atomically update the schema version and clear the upgrade step, if specified + // Note: This will need reworked if "updateSchemaVersion" is ever called within a transaction. + QSqlDatabase db = logDb(); + if (!beginTransaction(db)) { + qWarning() << "PostgreSqlStorage::updateSchemaVersion(int, bool): cannot start transaction!"; + qWarning() << " -" << qPrintable(db.lastError().text()); + return false; + } + + QSqlQuery query(db); query.prepare("UPDATE coreinfo SET value = :version WHERE key = 'schemaversion'"); query.bindValue(":version", newVersion); safeExec(query); - bool success = true; if (!watchQuery(query)) { - qCritical() << "PostgreSqlStorage::updateSchemaVersion(int): Updating schema version failed!"; - success = false; + qCritical() << "PostgreSqlStorage::updateSchemaVersion(int, bool): Updating schema version failed!"; + db.rollback(); + return false; } - return success; + + if (clearUpgradeStep) { + // Try clearing the upgrade step if requested + if (!setSchemaVersionUpgradeStep("")) { + db.rollback(); + return false; + } + } + + // Successful, commit and return true + db.commit(); + return true; } bool PostgreSqlStorage::setupSchemaVersion(int version) @@ -195,6 +217,50 @@ bool PostgreSqlStorage::setupSchemaVersion(int version) return success; } +QString PostgreSqlStorage::schemaVersionUpgradeStep() +{ + QSqlQuery query(logDb()); + query.prepare("SELECT value FROM coreinfo WHERE key = 'schemaupgradestep'"); + safeExec(query); + watchQuery(query); + if (query.first()) + return query.value(0).toString(); + + // Fall back to the default value + return AbstractSqlStorage::schemaVersionUpgradeStep(); +} + +bool PostgreSqlStorage::setSchemaVersionUpgradeStep(QString upgradeQuery) +{ + // Intentionally do not wrap in a transaction so other functions can include multiple operations + + QSqlQuery query(logDb()); + query.prepare("UPDATE coreinfo SET value = :upgradestep WHERE key = 'schemaupgradestep'"); + query.bindValue(":upgradestep", upgradeQuery); + safeExec(query); + + // Make sure that the query didn't fail (shouldn't ever happen), and that some non-zero number + // of rows were affected + bool success = watchQuery(query) && query.numRowsAffected() != 0; + + if (!success) { + // The key might not exist (Quassel 0.13.0 and older). Try inserting it... + query = QSqlQuery(logDb()); + query.prepare("INSERT INTO coreinfo (key, value) VALUES ('schemaupgradestep', :upgradestep)"); + query.bindValue(":upgradestep", upgradeQuery); + safeExec(query); + + if (!watchQuery(query)) { + qCritical() << Q_FUNC_INFO << "Setting schema upgrade step failed!"; + success = false; + } + else { + success = true; + } + } + return success; +} + UserId PostgreSqlStorage::addUser(const QString& user, const QString& password, const QString& authenticator) { QSqlQuery query(logDb()); @@ -610,9 +676,9 @@ void PostgreSqlStorage::removeIdentity(UserId user, IdentityId identityId) } } -QList PostgreSqlStorage::identities(UserId user) +std::vector PostgreSqlStorage::identities(UserId user) { - QList identities; + std::vector identities; QSqlDatabase db = logDb(); if (!beginReadOnlyTransaction(db)) { @@ -664,7 +730,7 @@ QList PostgreSqlStorage::identities(UserId user) nicks << nickQuery.value(0).toString(); } identity.setNicks(nicks); - identities << identity; + identities.push_back(std::move(identity)); } db.commit(); return identities; @@ -842,9 +908,9 @@ bool PostgreSqlStorage::removeNetwork(UserId user, const NetworkId& networkId) return true; } -QList PostgreSqlStorage::networks(UserId user) +std::vector PostgreSqlStorage::networks(UserId user) { - QList nets; + std::vector nets; QSqlDatabase db = logDb(); if (!beginReadOnlyTransaction(db)) { @@ -918,15 +984,15 @@ QList PostgreSqlStorage::networks(UserId user) servers << server; } net.serverList = servers; - nets << net; + nets.push_back(std::move(net)); } db.commit(); return nets; } -QList PostgreSqlStorage::connectedNetworks(UserId user) +std::vector PostgreSqlStorage::connectedNetworks(UserId user) { - QList connectedNets; + std::vector connectedNets; QSqlDatabase db = logDb(); if (!beginReadOnlyTransaction(db)) { @@ -942,7 +1008,7 @@ QList PostgreSqlStorage::connectedNetworks(UserId user) watchQuery(query); while (query.next()) { - connectedNets << query.value(0).toInt(); + connectedNets.emplace_back(query.value(0).toInt()); } db.commit(); @@ -1144,9 +1210,9 @@ BufferInfo PostgreSqlStorage::getBufferInfo(UserId user, const BufferId& bufferI return bufferInfo; } -QList PostgreSqlStorage::requestBuffers(UserId user) +std::vector PostgreSqlStorage::requestBuffers(UserId user) { - QList bufferlist; + std::vector bufferlist; QSqlDatabase db = logDb(); if (!beginReadOnlyTransaction(db)) { @@ -1162,19 +1228,19 @@ QList PostgreSqlStorage::requestBuffers(UserId user) safeExec(query); watchQuery(query); while (query.next()) { - bufferlist << BufferInfo(query.value(0).toInt(), - query.value(1).toInt(), - (BufferInfo::Type)query.value(2).toInt(), - query.value(3).toInt(), - query.value(4).toString()); + bufferlist.emplace_back(query.value(0).toInt(), + query.value(1).toInt(), + (BufferInfo::Type)query.value(2).toInt(), + query.value(3).toInt(), + query.value(4).toString()); } db.commit(); return bufferlist; } -QList PostgreSqlStorage::requestBufferIdsForNetwork(UserId user, NetworkId networkId) +std::vector PostgreSqlStorage::requestBufferIdsForNetwork(UserId user, NetworkId networkId) { - QList bufferList; + std::vector bufferList; QSqlDatabase db = logDb(); if (!beginReadOnlyTransaction(db)) { @@ -1191,7 +1257,7 @@ QList PostgreSqlStorage::requestBufferIdsForNetwork(UserId user, Netwo safeExec(query); watchQuery(query); while (query.next()) { - bufferList << BufferId(query.value(0).toInt()); + bufferList.emplace_back(query.value(0).toInt()); } db.commit(); return bufferList; @@ -1319,6 +1385,34 @@ bool PostgreSqlStorage::mergeBuffersPermanently(const UserId& user, const Buffer return true; } +QHash PostgreSqlStorage::bufferLastMsgIds(UserId user) +{ + QHash lastMsgHash; + + QSqlDatabase db = logDb(); + if (!beginReadOnlyTransaction(db)) { + qWarning() << "PostgreSqlStorage::bufferLastMsgIds(): cannot start read only transaction!"; + qWarning() << " -" << qPrintable(db.lastError().text()); + return lastMsgHash; + } + + QSqlQuery query(db); + query.prepare(queryString("select_buffer_last_messages")); + query.bindValue(":userid", user.toInt()); + safeExec(query); + if (!watchQuery(query)) { + db.rollback(); + return lastMsgHash; + } + + while (query.next()) { + lastMsgHash[query.value(0).toInt()] = query.value(1).toLongLong(); + } + + db.commit(); + return lastMsgHash; +} + void PostgreSqlStorage::setBufferLastSeenMsg(UserId user, const BufferId& bufferId, const MsgId& msgId) { QSqlQuery query(logDb()); @@ -1689,9 +1783,9 @@ bool PostgreSqlStorage::logMessages(MessageList& msgs) return true; } -QList PostgreSqlStorage::requestMsgs(UserId user, BufferId bufferId, MsgId first, MsgId last, int limit) +std::vector PostgreSqlStorage::requestMsgs(UserId user, BufferId bufferId, MsgId first, MsgId last, int limit) { - QList messagelist; + std::vector messagelist; QSqlDatabase db = logDb(); if (!beginReadOnlyTransaction(db)) { @@ -1750,17 +1844,17 @@ QList PostgreSqlStorage::requestMsgs(UserId user, BufferId bufferId, Ms query.value(7).toString(), (Message::Flags)query.value(3).toInt()); msg.setMsgId(query.value(0).toLongLong()); - messagelist << msg; + messagelist.push_back(std::move(msg)); } db.commit(); return messagelist; } -QList PostgreSqlStorage::requestMsgsFiltered( +std::vector PostgreSqlStorage::requestMsgsFiltered( UserId user, BufferId bufferId, MsgId first, MsgId last, int limit, Message::Types type, Message::Flags flags) { - QList messagelist; + std::vector messagelist; QSqlDatabase db = logDb(); if (!beginReadOnlyTransaction(db)) { @@ -1818,16 +1912,16 @@ QList PostgreSqlStorage::requestMsgsFiltered( query.value(7).toString(), Message::Flags{query.value(3).toInt()}); msg.setMsgId(query.value(0).toLongLong()); - messagelist << msg; + messagelist.push_back(std::move(msg)); } db.commit(); return messagelist; } -QList PostgreSqlStorage::requestAllMsgs(UserId user, MsgId first, MsgId last, int limit) +std::vector PostgreSqlStorage::requestAllMsgs(UserId user, MsgId first, MsgId last, int limit) { - QList messagelist; + std::vector messagelist; // requestBuffers uses it's own transaction. QHash bufferInfoHash; @@ -1874,17 +1968,17 @@ QList PostgreSqlStorage::requestAllMsgs(UserId user, MsgId first, MsgId query.value(8).toString(), (Message::Flags)query.value(4).toInt()); msg.setMsgId(query.value(0).toLongLong()); - messagelist << msg; + messagelist.push_back(std::move(msg)); } db.commit(); return messagelist; } -QList PostgreSqlStorage::requestAllMsgsFiltered( +std::vector PostgreSqlStorage::requestAllMsgsFiltered( UserId user, MsgId first, MsgId last, int limit, Message::Types type, Message::Flags flags) { - QList messagelist; + std::vector messagelist; // requestBuffers uses it's own transaction. QHash bufferInfoHash; @@ -1938,7 +2032,7 @@ QList PostgreSqlStorage::requestAllMsgsFiltered( query.value(8).toString(), Message::Flags{query.value(4).toInt()}); msg.setMsgId(query.value(0).toLongLong()); - messagelist << msg; + messagelist.push_back(std::move(msg)); } db.commit();