Sync last message id per buffer
[quassel.git] / src / core / postgresqlstorage.cpp
index 8b12396..90b0fdb 100644 (file)
@@ -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  *
 
 #include "postgresqlstorage.h"
 
-#include <QtSql>
+#include <QByteArray>
+#include <QDataStream>
+#include <QSqlDriver>
+#include <QSqlField>
 
 #include "network.h"
 #include "quassel.h"
@@ -164,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)
@@ -194,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());
@@ -609,9 +676,9 @@ void PostgreSqlStorage::removeIdentity(UserId user, IdentityId identityId)
     }
 }
 
-QList<CoreIdentity> PostgreSqlStorage::identities(UserId user)
+std::vector<CoreIdentity> PostgreSqlStorage::identities(UserId user)
 {
-    QList<CoreIdentity> identities;
+    std::vector<CoreIdentity> identities;
 
     QSqlDatabase db = logDb();
     if (!beginReadOnlyTransaction(db)) {
@@ -663,7 +730,7 @@ QList<CoreIdentity> PostgreSqlStorage::identities(UserId user)
             nicks << nickQuery.value(0).toString();
         }
         identity.setNicks(nicks);
-        identities << identity;
+        identities.push_back(std::move(identity));
     }
     db.commit();
     return identities;
@@ -841,9 +908,9 @@ bool PostgreSqlStorage::removeNetwork(UserId user, const NetworkId& networkId)
     return true;
 }
 
-QList<NetworkInfo> PostgreSqlStorage::networks(UserId user)
+std::vector<NetworkInfo> PostgreSqlStorage::networks(UserId user)
 {
-    QList<NetworkInfo> nets;
+    std::vector<NetworkInfo> nets;
 
     QSqlDatabase db = logDb();
     if (!beginReadOnlyTransaction(db)) {
@@ -917,15 +984,15 @@ QList<NetworkInfo> PostgreSqlStorage::networks(UserId user)
             servers << server;
         }
         net.serverList = servers;
-        nets << net;
+        nets.push_back(std::move(net));
     }
     db.commit();
     return nets;
 }
 
-QList<NetworkId> PostgreSqlStorage::connectedNetworks(UserId user)
+std::vector<NetworkId> PostgreSqlStorage::connectedNetworks(UserId user)
 {
-    QList<NetworkId> connectedNets;
+    std::vector<NetworkId> connectedNets;
 
     QSqlDatabase db = logDb();
     if (!beginReadOnlyTransaction(db)) {
@@ -941,7 +1008,7 @@ QList<NetworkId> PostgreSqlStorage::connectedNetworks(UserId user)
     watchQuery(query);
 
     while (query.next()) {
-        connectedNets << query.value(0).toInt();
+        connectedNets.emplace_back(query.value(0).toInt());
     }
 
     db.commit();
@@ -1143,9 +1210,9 @@ BufferInfo PostgreSqlStorage::getBufferInfo(UserId user, const BufferId& bufferI
     return bufferInfo;
 }
 
-QList<BufferInfo> PostgreSqlStorage::requestBuffers(UserId user)
+std::vector<BufferInfo> PostgreSqlStorage::requestBuffers(UserId user)
 {
-    QList<BufferInfo> bufferlist;
+    std::vector<BufferInfo> bufferlist;
 
     QSqlDatabase db = logDb();
     if (!beginReadOnlyTransaction(db)) {
@@ -1161,19 +1228,19 @@ QList<BufferInfo> 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<BufferId> PostgreSqlStorage::requestBufferIdsForNetwork(UserId user, NetworkId networkId)
+std::vector<BufferId> PostgreSqlStorage::requestBufferIdsForNetwork(UserId user, NetworkId networkId)
 {
-    QList<BufferId> bufferList;
+    std::vector<BufferId> bufferList;
 
     QSqlDatabase db = logDb();
     if (!beginReadOnlyTransaction(db)) {
@@ -1190,7 +1257,7 @@ QList<BufferId> 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;
@@ -1318,6 +1385,34 @@ bool PostgreSqlStorage::mergeBuffersPermanently(const UserId& user, const Buffer
     return true;
 }
 
+QHash<BufferId, MsgId> PostgreSqlStorage::bufferLastMsgIds(UserId user)
+{
+    QHash<BufferId, MsgId> 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());
@@ -1688,9 +1783,9 @@ bool PostgreSqlStorage::logMessages(MessageList& msgs)
     return true;
 }
 
-QList<Message> PostgreSqlStorage::requestMsgs(UserId user, BufferId bufferId, MsgId first, MsgId last, int limit)
+std::vector<Message> PostgreSqlStorage::requestMsgs(UserId user, BufferId bufferId, MsgId first, MsgId last, int limit)
 {
-    QList<Message> messagelist;
+    std::vector<Message> messagelist;
 
     QSqlDatabase db = logDb();
     if (!beginReadOnlyTransaction(db)) {
@@ -1749,17 +1844,17 @@ QList<Message> 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<Message> PostgreSqlStorage::requestMsgsFiltered(
+std::vector<Message> PostgreSqlStorage::requestMsgsFiltered(
     UserId user, BufferId bufferId, MsgId first, MsgId last, int limit, Message::Types type, Message::Flags flags)
 {
-    QList<Message> messagelist;
+    std::vector<Message> messagelist;
 
     QSqlDatabase db = logDb();
     if (!beginReadOnlyTransaction(db)) {
@@ -1817,16 +1912,16 @@ QList<Message> 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<Message> PostgreSqlStorage::requestAllMsgs(UserId user, MsgId first, MsgId last, int limit)
+std::vector<Message> PostgreSqlStorage::requestAllMsgs(UserId user, MsgId first, MsgId last, int limit)
 {
-    QList<Message> messagelist;
+    std::vector<Message> messagelist;
 
     // requestBuffers uses it's own transaction.
     QHash<BufferId, BufferInfo> bufferInfoHash;
@@ -1873,17 +1968,17 @@ QList<Message> 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<Message> PostgreSqlStorage::requestAllMsgsFiltered(
+std::vector<Message> PostgreSqlStorage::requestAllMsgsFiltered(
     UserId user, MsgId first, MsgId last, int limit, Message::Types type, Message::Flags flags)
 {
-    QList<Message> messagelist;
+    std::vector<Message> messagelist;
 
     // requestBuffers uses it's own transaction.
     QHash<BufferId, BufferInfo> bufferInfoHash;
@@ -1937,7 +2032,7 @@ QList<Message> 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();