X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fcore%2Fpostgresqlstorage.cpp;h=030423f6e409c68a0283e28abd9210a11f888836;hp=200fd14ca5bde5087fea0357fcd5122849bdb9ac;hb=99055e8e4a83c234ae424cc225d3a7aa17c1544b;hpb=c1cf157116de7fc3da96203aa6f03c38c7ebb650 diff --git a/src/core/postgresqlstorage.cpp b/src/core/postgresqlstorage.cpp index 200fd14c..030423f6 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,8 +48,8 @@ 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:")) - << qPrintable(QSqlDatabase::drivers().join(", ")); + qWarning() << qPrintable(tr("PostgreSQL driver plugin not available for Qt. Installed drivers:")) + << qPrintable(QSqlDatabase::drivers().join(", ")); return false; } return true; @@ -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,15 @@ 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 +124,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 +168,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 +218,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()); @@ -461,13 +528,8 @@ IdentityId PostgreSqlStorage::createIdentity(UserId user, CoreIdentity& identity query.bindValue(":kickreason", identity.kickReason()); query.bindValue(":partreason", identity.partReason()); query.bindValue(":quitreason", identity.quitReason()); -#ifdef HAVE_SSL query.bindValue(":sslcert", identity.sslCert().toPem()); query.bindValue(":sslkey", identity.sslKey().toPem()); -#else - query.bindValue(":sslcert", QByteArray()); - query.bindValue(":sslkey", QByteArray()); -#endif safeExec(query); if (!watchQuery(query)) { db.rollback(); @@ -544,13 +606,8 @@ bool PostgreSqlStorage::updateIdentity(UserId user, const CoreIdentity& identity query.bindValue(":kickreason", identity.kickReason()); query.bindValue(":partreason", identity.partReason()); query.bindValue(":quitreason", identity.quitReason()); -#ifdef HAVE_SSL query.bindValue(":sslcert", identity.sslCert().toPem()); query.bindValue(":sslkey", identity.sslKey().toPem()); -#else - query.bindValue(":sslcert", QByteArray()); - query.bindValue(":sslkey", QByteArray()); -#endif query.bindValue(":identityid", identity.id().toInt()); safeExec(query); @@ -610,9 +667,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)) { @@ -651,10 +708,8 @@ QList PostgreSqlStorage::identities(UserId user) identity.setKickReason(query.value(15).toString()); identity.setPartReason(query.value(16).toString()); identity.setQuitReason(query.value(17).toString()); -#ifdef HAVE_SSL identity.setSslCert(query.value(18).toByteArray()); identity.setSslKey(query.value(19).toByteArray()); -#endif nickQuery.bindValue(":identityid", identity.id().toInt()); QList nicks; @@ -664,7 +719,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; @@ -745,6 +800,8 @@ void PostgreSqlStorage::bindNetworkInfo(QSqlQuery& query, const NetworkInfo& inf query.bindValue(":messagerateburstsize", info.messageRateBurstSize); query.bindValue(":messageratedelay", info.messageRateDelay); query.bindValue(":unlimitedmessagerate", info.unlimitedMessageRate); + query.bindValue(":skipcaps", info.skipCapsToString()); + if (info.networkId.isValid()) query.bindValue(":networkid", info.networkId.toInt()); } @@ -842,9 +899,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)) { @@ -892,6 +949,7 @@ QList PostgreSqlStorage::networks(UserId user) net.messageRateBurstSize = networksQuery.value(20).toUInt(); net.messageRateDelay = networksQuery.value(21).toUInt(); net.unlimitedMessageRate = networksQuery.value(22).toBool(); + net.skipCapsFromString(networksQuery.value(23).toString()); serversQuery.bindValue(":networkid", net.networkId.toInt()); safeExec(serversQuery); @@ -918,15 +976,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 +1000,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 +1202,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 +1220,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 +1249,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 +1377,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()); @@ -1447,7 +1533,7 @@ Message::Types PostgreSqlStorage::bufferActivity(BufferId bufferId, MsgId lastSe query.bindValue(":lastseenmsgid", lastSeenMsgId.toQint64()); safeExec(query); watchQuery(query); - Message::Types result = Message::Types(nullptr); + Message::Types result{}; if (query.first()) result = Message::Types(query.value(0).toInt()); return result; @@ -1689,9 +1775,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 +1836,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 +1904,92 @@ 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; +} + +std::vector PostgreSqlStorage::requestMsgsForward( + UserId user, BufferId bufferId, MsgId first, MsgId last, int limit, Message::Types type, Message::Flags flags) +{ + std::vector messagelist; + + QSqlDatabase db = logDb(); + if (!beginReadOnlyTransaction(db)) { + qWarning() << "PostgreSqlStorage::requestMsgsForward(): cannot start read only transaction!"; + qWarning() << " -" << qPrintable(db.lastError().text()); + return messagelist; + } + + BufferInfo bufferInfo = getBufferInfo(user, bufferId); + if (!bufferInfo.isValid()) { + db.rollback(); + return messagelist; + } + + QString queryName; + QVariantList params; + + if (first == -1) { + params << std::numeric_limits::min(); + } else { + params << first.toQint64(); + } + + if (last == -1) { + params << std::numeric_limits::max(); + } else { + params << last.toQint64(); + } + + params << bufferId.toInt(); + + int typeRaw = type; + int flagsRaw = flags; + params << typeRaw; + params << flagsRaw; + + if (limit != -1) + params << limit; + else + params << QVariant(QVariant::Int); + + QSqlQuery query = executePreparedQuery("select_messagesForward", params, db); + + if (!watchQuery(query)) { + qDebug() << "select_messages failed"; + db.rollback(); + return messagelist; + } + + QDateTime timestamp; + while (query.next()) { + // PostgreSQL returns date/time in ISO 8601 format, no 64-bit handling needed + // See https://www.postgresql.org/docs/current/static/datatype-datetime.html#DATATYPE-DATETIME-OUTPUT + timestamp = query.value(1).toDateTime(); + timestamp.setTimeSpec(Qt::UTC); + Message msg(timestamp, + bufferInfo, + (Message::Type)query.value(2).toInt(), + query.value(8).toString(), + query.value(4).toString(), + query.value(5).toString(), + query.value(6).toString(), + query.value(7).toString(), + (Message::Flags)query.value(3).toInt()); + msg.setMsgId(query.value(0).toLongLong()); + 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 +2036,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 +2100,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(); @@ -2265,6 +2427,8 @@ bool PostgreSqlMigrationWriter::writeMo(const NetworkMO& network) bindValue(26, network.messagerateburstsize); bindValue(27, network.messageratedelay); bindValue(28, network.unlimitedmessagerate); + // Skipped IRCv3 caps + bindValue(29, network.skipcaps); return exec(); }