Sync last message id per buffer
[quassel.git] / src / core / sqlitestorage.cpp
index 1503015..dc5b546 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 "sqlitestorage.h"
 
-#include <QtSql>
+#include <QByteArray>
+#include <QDataStream>
+#include <QLatin1String>
+#include <QVariant>
 
-#include "logmessage.h"
 #include "network.h"
 #include "quassel.h"
 
 int SqliteStorage::_maxRetryCount = 150;
 
-SqliteStorage::SqliteStorage(QObject *parent)
+SqliteStorage::SqliteStorage(QObjectparent)
     : AbstractSqlStorage(parent)
-{
-}
-
-
-SqliteStorage::~SqliteStorage()
-{
-}
-
+{}
 
 bool SqliteStorage::isAvailable() const
 {
-    if (!QSqlDatabase::isDriverAvailable("QSQLITE")) return false;
+    if (!QSqlDatabase::isDriverAvailable("QSQLITE"))
+        return false;
     return true;
 }
 
-
 QString SqliteStorage::backendId() const
 {
     return QString("SQLite");
 }
 
-
 QString SqliteStorage::displayName() const
 {
     // Note: Pre-0.13 clients use the displayName property for backend idenfication
@@ -61,7 +55,6 @@ QString SqliteStorage::displayName() const
     return backendId();
 }
 
-
 QString SqliteStorage::description() const
 {
     return tr("SQLite is a file-based database engine that does not require any setup. It is suitable for small and medium-sized "
@@ -69,7 +62,6 @@ QString SqliteStorage::description() const
               "it is running on, and if you only expect a few users to use your core.");
 }
 
-
 int SqliteStorage::installedSchemaVersion()
 {
     // only used when there is a singlethread (during startup)
@@ -86,24 +78,40 @@ int SqliteStorage::installedSchemaVersion()
     return AbstractSqlStorage::installedSchemaVersion();
 }
 
-
-bool SqliteStorage::updateSchemaVersion(int newVersion)
+bool SqliteStorage::updateSchemaVersion(int newVersion, bool clearUpgradeStep)
 {
     // only used when there is a singlethread (during startup)
     // so we don't need locking here
-    QSqlQuery query(logDb());
+
+    QSqlDatabase db = 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.
+    db.transaction();
+
+    QSqlQuery query(db);
     query.prepare("UPDATE coreinfo SET value = :version WHERE key = 'schemaversion'");
     query.bindValue(":version", newVersion);
-    query.exec();
+    safeExec(query);
 
-    bool success = true;
-    if (query.lastError().isValid()) {
-        qCritical() << "SqliteStorage::updateSchemaVersion(int): Updating schema version failed!";
-        success = false;
+    if (!watchQuery(query)) {
+        qCritical() << "SqliteStorage::updateSchemaVersion(int, bool): Updating schema version failed!";
+        db.rollback();
+        return false;
+    }
+
+    if (clearUpgradeStep) {
+        // Try clearing the upgrade step if requested
+        if (!setSchemaVersionUpgradeStep("")) {
+            db.rollback();
+            return false;
+        }
     }
-    return success;
-}
 
+    // Successful, commit and return true
+    db.commit();
+    return true;
+}
 
 bool SqliteStorage::setupSchemaVersion(int version)
 {
@@ -122,8 +130,53 @@ bool SqliteStorage::setupSchemaVersion(int version)
     return success;
 }
 
+QString SqliteStorage::schemaVersionUpgradeStep()
+{
+    // Only used when there is a singlethread (during startup), so we don't need locking here
+    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 SqliteStorage::setSchemaVersionUpgradeStep(QString upgradeQuery)
+{
+    // Only used when there is a singlethread (during startup), so we don't need locking here
+
+    // 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);
+
+    // Don't wrap with watchQuery to avoid an alarming message in the log when the key is missing
+    // Make sure that the query didn't fail, and that some non-zero number of rows were affected
+    bool success = !query.lastError().isValid() && 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 SqliteStorage::addUser(const QString &user, const QString &password, const QString &authenticator)
+UserId SqliteStorage::addUser(const QString& user, const QString& password, const QString& authenticator)
 {
     QSqlDatabase db = logDb();
     UserId uid;
@@ -141,7 +194,8 @@ UserId SqliteStorage::addUser(const QString &user, const QString &password, cons
         query.bindValue(":authenticator", authenticator);
         lockForWrite();
         safeExec(query);
-        if (query.lastError().isValid() && query.lastError().number() == 19) { // user already exists - sadly 19 seems to be the general constraint violation error...
+        if (query.lastError().isValid()
+            && query.lastError().nativeErrorCode() == QLatin1String{"19"}) {  // user already exists - sadly 19 seems to be the general constraint violation error...
             db.rollback();
         }
         else {
@@ -156,8 +210,7 @@ UserId SqliteStorage::addUser(const QString &user, const QString &password, cons
     return uid;
 }
 
-
-bool SqliteStorage::updateUser(UserId user, const QString &password)
+bool SqliteStorage::updateUser(UserId user, const QString& password)
 {
     QSqlDatabase db = logDb();
     bool success = false;
@@ -178,8 +231,7 @@ bool SqliteStorage::updateUser(UserId user, const QString &password)
     return success;
 }
 
-
-void SqliteStorage::renameUser(UserId user, const QString &newName)
+void SqliteStorage::renameUser(UserId user, const QString& newName)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -196,8 +248,7 @@ void SqliteStorage::renameUser(UserId user, const QString &newName)
     emit userRenamed(user, newName);
 }
 
-
-UserId SqliteStorage::validateUser(const QString &user, const QString &password)
+UserId SqliteStorage::validateUser(const QString& user, const QString& password)
 {
     UserId userId;
     QString hashedPassword;
@@ -226,8 +277,7 @@ UserId SqliteStorage::validateUser(const QString &user, const QString &password)
     return returnUserId;
 }
 
-
-UserId SqliteStorage::getUserId(const QString &username)
+UserId SqliteStorage::getUserId(const QString& username)
 {
     UserId userId;
 
@@ -288,7 +338,6 @@ UserId SqliteStorage::internalUser()
     return userId;
 }
 
-
 void SqliteStorage::delUser(UserId user)
 {
     QSqlDatabase db = logDb();
@@ -320,8 +369,7 @@ void SqliteStorage::delUser(UserId user)
     emit userRemoved(user);
 }
 
-
-void SqliteStorage::setUserSetting(UserId userId, const QString &settingName, const QVariant &data)
+void SqliteStorage::setUserSetting(UserId userId, const QString& settingName, const QVariant& data)
 {
     QByteArray rawData;
     QDataStream out(&rawData, QIODevice::WriteOnly);
@@ -352,8 +400,7 @@ void SqliteStorage::setUserSetting(UserId userId, const QString &settingName, co
     unlock();
 }
 
-
-QVariant SqliteStorage::getUserSetting(UserId userId, const QString &settingName, const QVariant &defaultData)
+QVariant SqliteStorage::getUserSetting(UserId userId, const QString& settingName, const QVariant& defaultData)
 {
     QVariant data = defaultData;
     {
@@ -375,8 +422,7 @@ QVariant SqliteStorage::getUserSetting(UserId userId, const QString &settingName
     return data;
 }
 
-
-void SqliteStorage::setCoreState(const QVariantList &data)
+void SqliteStorage::setCoreState(const QVariantList& data)
 {
     QByteArray rawData;
     QDataStream out(&rawData, QIODevice::WriteOnly);
@@ -405,8 +451,7 @@ void SqliteStorage::setCoreState(const QVariantList &data)
     unlock();
 }
 
-
-QVariantList SqliteStorage::getCoreState(const QVariantList &defaultData)
+QVariantList SqliteStorage::getCoreState(const QVariantList& defaultData)
 {
     QVariantList data;
     {
@@ -421,7 +466,8 @@ QVariantList SqliteStorage::getCoreState(const QVariantList &defaultData)
             QDataStream in(&rawData, QIODevice::ReadOnly);
             in.setVersion(QDataStream::Qt_4_2);
             in >> data;
-        } else {
+        }
+        else {
             data = defaultData;
         }
     }
@@ -429,8 +475,7 @@ QVariantList SqliteStorage::getCoreState(const QVariantList &defaultData)
     return data;
 }
 
-
-IdentityId SqliteStorage::createIdentity(UserId user, CoreIdentity &identity)
+IdentityId SqliteStorage::createIdentity(UserId user, CoreIdentity& identity)
 {
     IdentityId identityId;
 
@@ -481,7 +526,7 @@ IdentityId SqliteStorage::createIdentity(UserId user, CoreIdentity &identity)
 
             QSqlQuery insertNickQuery(db);
             insertNickQuery.prepare(queryString("insert_nick"));
-            foreach(QString nick, identity.nicks()) {
+            foreach (QString nick, identity.nicks()) {
                 insertNickQuery.bindValue(":identityid", identityId.toInt());
                 insertNickQuery.bindValue(":nick", nick);
                 safeExec(insertNickQuery);
@@ -494,8 +539,7 @@ IdentityId SqliteStorage::createIdentity(UserId user, CoreIdentity &identity)
     return identityId;
 }
 
-
-bool SqliteStorage::updateIdentity(UserId user, const CoreIdentity &identity)
+bool SqliteStorage::updateIdentity(UserId user, const CoreIdentity& identity)
 {
     QSqlDatabase db = logDb();
     bool error = false;
@@ -556,7 +600,7 @@ bool SqliteStorage::updateIdentity(UserId user, const CoreIdentity &identity)
 
         QSqlQuery insertNickQuery(db);
         insertNickQuery.prepare(queryString("insert_nick"));
-        foreach(QString nick, identity.nicks()) {
+        foreach (QString nick, identity.nicks()) {
             insertNickQuery.bindValue(":identityid", identity.id().toInt());
             insertNickQuery.bindValue(":nick", nick);
             safeExec(insertNickQuery);
@@ -568,7 +612,6 @@ bool SqliteStorage::updateIdentity(UserId user, const CoreIdentity &identity)
     return true;
 }
 
-
 void SqliteStorage::removeIdentity(UserId user, IdentityId identityId)
 {
     QSqlDatabase db = logDb();
@@ -607,10 +650,9 @@ void SqliteStorage::removeIdentity(UserId user, IdentityId identityId)
     unlock();
 }
 
-
-QList<CoreIdentity> SqliteStorage::identities(UserId user)
+std::vector<CoreIdentity> SqliteStorage::identities(UserId user)
 {
-    QList<CoreIdentity> identities;
+    std::vector<CoreIdentity> identities;
     QSqlDatabase db = logDb();
     db.transaction();
 
@@ -657,8 +699,8 @@ QList<CoreIdentity> SqliteStorage::identities(UserId user)
             while (nickQuery.next()) {
                 nicks << nickQuery.value(0).toString();
             }
-            identity.setNicks(nicks);
-            identities << identity;
+            identity.setNicks(std::move(nicks));
+            identities.push_back(std::move(identity));
         }
         db.commit();
     }
@@ -666,8 +708,7 @@ QList<CoreIdentity> SqliteStorage::identities(UserId user)
     return identities;
 }
 
-
-NetworkId SqliteStorage::createNetwork(UserId user, const NetworkInfo &info)
+NetworkId SqliteStorage::createNetwork(UserId user, const NetworkInfo& info)
 {
     NetworkId networkId;
 
@@ -692,13 +733,13 @@ NetworkId SqliteStorage::createNetwork(UserId user, const NetworkInfo &info)
     }
     if (error) {
         unlock();
-        return NetworkId();
+        return {};
     }
 
     {
         QSqlQuery insertServersQuery(db);
         insertServersQuery.prepare(queryString("insert_server"));
-        foreach(Network::Server server, info.serverList) {
+        foreach (Network::Server server, info.serverList) {
             insertServersQuery.bindValue(":userid", user.toInt());
             insertServersQuery.bindValue(":networkid", networkId.toInt());
             bindServerInfo(insertServersQuery, server);
@@ -714,13 +755,12 @@ NetworkId SqliteStorage::createNetwork(UserId user, const NetworkInfo &info)
     }
     unlock();
     if (error)
-        return NetworkId();
+        return {};
     else
         return networkId;
 }
 
-
-void SqliteStorage::bindNetworkInfo(QSqlQuery &query, const NetworkInfo &info)
+void SqliteStorage::bindNetworkInfo(QSqlQuery& query, const NetworkInfo& info)
 {
     query.bindValue(":networkname", info.networkName);
     query.bindValue(":identityid", info.identity.toInt());
@@ -749,8 +789,7 @@ void SqliteStorage::bindNetworkInfo(QSqlQuery &query, const NetworkInfo &info)
         query.bindValue(":networkid", info.networkId.toInt());
 }
 
-
-void SqliteStorage::bindServerInfo(QSqlQuery &query, const Network::Server &server)
+void SqliteStorage::bindServerInfo(QSqlQuery& query, const Network::Server& server)
 {
     query.bindValue(":hostname", server.host);
     query.bindValue(":port", server.port);
@@ -766,8 +805,7 @@ void SqliteStorage::bindServerInfo(QSqlQuery &query, const Network::Server &serv
     query.bindValue(":sslverify", server.sslVerify ? 1 : 0);
 }
 
-
-bool SqliteStorage::updateNetwork(UserId user, const NetworkInfo &info)
+bool SqliteStorage::updateNetwork(UserId user, const NetworkInfo& info)
 {
     QSqlDatabase db = logDb();
     bool error = false;
@@ -809,7 +847,7 @@ bool SqliteStorage::updateNetwork(UserId user, const NetworkInfo &info)
     {
         QSqlQuery insertServersQuery(db);
         insertServersQuery.prepare(queryString("insert_server"));
-        foreach(Network::Server server, info.serverList) {
+        foreach (Network::Server server, info.serverList) {
             insertServersQuery.bindValue(":userid", user.toInt());
             insertServersQuery.bindValue(":networkid", info.networkId.toInt());
             bindServerInfo(insertServersQuery, server);
@@ -827,8 +865,7 @@ bool SqliteStorage::updateNetwork(UserId user, const NetworkInfo &info)
     return !error;
 }
 
-
-bool SqliteStorage::removeNetwork(UserId user, const NetworkId &networkId)
+bool SqliteStorage::removeNetwork(UserId user, const NetworkId& networkId)
 {
     QSqlDatabase db = logDb();
     bool error = false;
@@ -901,10 +938,9 @@ bool SqliteStorage::removeNetwork(UserId user, const NetworkId &networkId)
     return true;
 }
 
-
-QList<NetworkInfo> SqliteStorage::networks(UserId user)
+std::vector<NetworkInfo> SqliteStorage::networks(UserId user)
 {
-    QList<NetworkInfo> nets;
+    std::vector<NetworkInfo> nets;
 
     QSqlDatabase db = logDb();
     db.transaction();
@@ -972,7 +1008,7 @@ QList<NetworkInfo> SqliteStorage::networks(UserId user)
                         servers << server;
                     }
                     net.serverList = servers;
-                    nets << net;
+                    nets.push_back(std::move(net));
                 }
             }
         }
@@ -982,10 +1018,9 @@ QList<NetworkInfo> SqliteStorage::networks(UserId user)
     return nets;
 }
 
-
-QList<NetworkId> SqliteStorage::connectedNetworks(UserId user)
+std::vector<NetworkId> SqliteStorage::connectedNetworks(UserId user)
 {
-    QList<NetworkId> connectedNets;
+    std::vector<NetworkId> connectedNets;
 
     QSqlDatabase db = logDb();
     db.transaction();
@@ -999,7 +1034,7 @@ QList<NetworkId> SqliteStorage::connectedNetworks(UserId user)
         watchQuery(query);
 
         while (query.next()) {
-            connectedNets << query.value(0).toInt();
+            connectedNets.emplace_back(query.value(0).toInt());
         }
         db.commit();
     }
@@ -1007,8 +1042,7 @@ QList<NetworkId> SqliteStorage::connectedNetworks(UserId user)
     return connectedNets;
 }
 
-
-void SqliteStorage::setNetworkConnected(UserId user, const NetworkId &networkId, bool isConnected)
+void SqliteStorage::setNetworkConnected(UserId user, const NetworkId& networkId, bool isConnected)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1028,8 +1062,7 @@ void SqliteStorage::setNetworkConnected(UserId user, const NetworkId &networkId,
     unlock();
 }
 
-
-QHash<QString, QString> SqliteStorage::persistentChannels(UserId user, const NetworkId &networkId)
+QHash<QString, QString> SqliteStorage::persistentChannels(UserId user, const NetworkId& networkId)
 {
     QHash<QString, QString> persistentChans;
 
@@ -1052,8 +1085,7 @@ QHash<QString, QString> SqliteStorage::persistentChannels(UserId user, const Net
     return persistentChans;
 }
 
-
-void SqliteStorage::setChannelPersistent(UserId user, const NetworkId &networkId, const QString &channel, bool isJoined)
+void SqliteStorage::setChannelPersistent(UserId user, const NetworkId& networkId, const QString& channel, bool isJoined)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1074,8 +1106,7 @@ void SqliteStorage::setChannelPersistent(UserId user, const NetworkId &networkId
     unlock();
 }
 
-
-void SqliteStorage::setPersistentChannelKey(UserId user, const NetworkId &networkId, const QString &channel, const QString &key)
+void SqliteStorage::setPersistentChannelKey(UserId user, const NetworkId& networkId, const QString& channel, const QString& key)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1096,7 +1127,6 @@ void SqliteStorage::setPersistentChannelKey(UserId user, const NetworkId &networ
     unlock();
 }
 
-
 QString SqliteStorage::awayMessage(UserId user, NetworkId networkId)
 {
     QSqlDatabase db = logDb();
@@ -1121,8 +1151,7 @@ QString SqliteStorage::awayMessage(UserId user, NetworkId networkId)
     return awayMsg;
 }
 
-
-void SqliteStorage::setAwayMessage(UserId user, NetworkId networkId, const QString &awayMsg)
+void SqliteStorage::setAwayMessage(UserId user, NetworkId networkId, const QString& awayMsg)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1142,7 +1171,6 @@ void SqliteStorage::setAwayMessage(UserId user, NetworkId networkId, const QStri
     unlock();
 }
 
-
 QString SqliteStorage::userModes(UserId user, NetworkId networkId)
 {
     QSqlDatabase db = logDb();
@@ -1167,8 +1195,7 @@ QString SqliteStorage::userModes(UserId user, NetworkId networkId)
     return modes;
 }
 
-
-void SqliteStorage::setUserModes(UserId user, NetworkId networkId, const QString &userModes)
+void SqliteStorage::setUserModes(UserId user, NetworkId networkId, const QString& userModes)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1188,8 +1215,7 @@ void SqliteStorage::setUserModes(UserId user, NetworkId networkId, const QString
     unlock();
 }
 
-
-BufferInfo SqliteStorage::bufferInfo(UserId user, const NetworkId &networkId, BufferInfo::Type type, const QString &buffer, bool create)
+BufferInfo SqliteStorage::bufferInfo(UserId user, const NetworkId& networkId, BufferInfo::Type type, const QString& buffer, bool create)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1240,8 +1266,7 @@ BufferInfo SqliteStorage::bufferInfo(UserId user, const NetworkId &networkId, Bu
     return bufferInfo;
 }
 
-
-BufferInfo SqliteStorage::getBufferInfo(UserId user, const BufferId &bufferId)
+BufferInfo SqliteStorage::getBufferInfo(UserId user, const BufferId& bufferId)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1257,7 +1282,11 @@ BufferInfo SqliteStorage::getBufferInfo(UserId user, const BufferId &bufferId)
         safeExec(query);
 
         if (watchQuery(query) && query.first()) {
-            bufferInfo = BufferInfo(query.value(0).toInt(), query.value(1).toInt(), (BufferInfo::Type)query.value(2).toInt(), 0, query.value(4).toString());
+            bufferInfo = BufferInfo(query.value(0).toInt(),
+                                    query.value(1).toInt(),
+                                    (BufferInfo::Type)query.value(2).toInt(),
+                                    0,
+                                    query.value(4).toString());
             Q_ASSERT(!query.next());
         }
         db.commit();
@@ -1266,10 +1295,9 @@ BufferInfo SqliteStorage::getBufferInfo(UserId user, const BufferId &bufferId)
     return bufferInfo;
 }
 
-
-QList<BufferInfo> SqliteStorage::requestBuffers(UserId user)
+std::vector<BufferInfo> SqliteStorage::requestBuffers(UserId user)
 {
-    QList<BufferInfo> bufferlist;
+    std::vector<BufferInfo> bufferlist;
 
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1283,7 +1311,11 @@ QList<BufferInfo> SqliteStorage::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();
     }
@@ -1292,10 +1324,9 @@ QList<BufferInfo> SqliteStorage::requestBuffers(UserId user)
     return bufferlist;
 }
 
-
-QList<BufferId> SqliteStorage::requestBufferIdsForNetwork(UserId user, NetworkId networkId)
+std::vector<BufferId> SqliteStorage::requestBufferIdsForNetwork(UserId user, NetworkId networkId)
 {
-    QList<BufferId> bufferList;
+    std::vector<BufferId> bufferList;
 
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1310,7 +1341,7 @@ QList<BufferId> SqliteStorage::requestBufferIdsForNetwork(UserId user, NetworkId
         safeExec(query);
         watchQuery(query);
         while (query.next()) {
-            bufferList << BufferId(query.value(0).toInt());
+            bufferList.emplace_back(query.value(0).toInt());
         }
         db.commit();
     }
@@ -1319,8 +1350,7 @@ QList<BufferId> SqliteStorage::requestBufferIdsForNetwork(UserId user, NetworkId
     return bufferList;
 }
 
-
-bool SqliteStorage::removeBuffer(const UserId &user, const BufferId &bufferId)
+bool SqliteStorage::removeBuffer(const UserId& user, const BufferId& bufferId)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1363,8 +1393,7 @@ bool SqliteStorage::removeBuffer(const UserId &user, const BufferId &bufferId)
     return !error;
 }
 
-
-bool SqliteStorage::renameBuffer(const UserId &user, const BufferId &bufferId, const QString &newName)
+bool SqliteStorage::renameBuffer(const UserId& user, const BufferId& bufferId, const QString& newName)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1383,7 +1412,7 @@ bool SqliteStorage::renameBuffer(const UserId &user, const BufferId &bufferId, c
 
         error = query.lastError().isValid();
         // unexepcted error occured (19 == constraint violation)
-        if (error && query.lastError().number() != 19) {
+        if (error && query.lastError().nativeErrorCode() != QLatin1String{"19"}) {
             watchQuery(query);
         }
         else {
@@ -1400,8 +1429,7 @@ bool SqliteStorage::renameBuffer(const UserId &user, const BufferId &bufferId, c
     return !error;
 }
 
-
-bool SqliteStorage::mergeBuffersPermanently(const UserId &user, const BufferId &bufferId1, const BufferId &bufferId2)
+bool SqliteStorage::mergeBuffersPermanently(const UserId& user, const BufferId& bufferId1, const BufferId& bufferId2)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1457,8 +1485,35 @@ bool SqliteStorage::mergeBuffersPermanently(const UserId &user, const BufferId &
     return !error;
 }
 
+QHash<BufferId, MsgId> SqliteStorage::bufferLastMsgIds(UserId user)
+{
+    QHash<BufferId, MsgId> lastMsgHash;
+
+    QSqlDatabase db = logDb();
+    db.transaction();
+
+    bool error = false;
+    {
+        QSqlQuery query(db);
+        query.prepare(queryString("select_buffer_last_messages"));
+        query.bindValue(":userid", user.toInt());
+
+        lockForRead();
+        safeExec(query);
+        error = !watchQuery(query);
+        if (!error) {
+            while (query.next()) {
+                lastMsgHash[query.value(0).toInt()] = query.value(1).toLongLong();
+            }
+        }
+    }
+
+    db.commit();
+    unlock();
+    return lastMsgHash;
+}
 
-void SqliteStorage::setBufferLastSeenMsg(UserId user, const BufferId &bufferId, const MsgId &msgId)
+void SqliteStorage::setBufferLastSeenMsg(UserId user, const BufferId& bufferId, const MsgId& msgId)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1478,7 +1533,6 @@ void SqliteStorage::setBufferLastSeenMsg(UserId user, const BufferId &bufferId,
     unlock();
 }
 
-
 QHash<BufferId, MsgId> SqliteStorage::bufferLastSeenMsgIds(UserId user)
 {
     QHash<BufferId, MsgId> lastSeenHash;
@@ -1507,8 +1561,7 @@ QHash<BufferId, MsgId> SqliteStorage::bufferLastSeenMsgIds(UserId user)
     return lastSeenHash;
 }
 
-
-void SqliteStorage::setBufferMarkerLineMsg(UserId user, const BufferId &bufferId, const MsgId &msgId)
+void SqliteStorage::setBufferMarkerLineMsg(UserId user, const BufferId& bufferId, const MsgId& msgId)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1528,7 +1581,6 @@ void SqliteStorage::setBufferMarkerLineMsg(UserId user, const BufferId &bufferId
     unlock();
 }
 
-
 QHash<BufferId, MsgId> SqliteStorage::bufferMarkerLineMsgIds(UserId user)
 {
     QHash<BufferId, MsgId> markerLineHash;
@@ -1567,7 +1619,7 @@ void SqliteStorage::setBufferActivity(UserId user, BufferId bufferId, Message::T
         query.prepare(queryString("update_buffer_bufferactivity"));
         query.bindValue(":userid", user.toInt());
         query.bindValue(":bufferid", bufferId.toInt());
-        query.bindValue(":bufferactivity", (int) bufferActivity);
+        query.bindValue(":bufferactivity", (int)bufferActivity);
 
         lockForWrite();
         safeExec(query);
@@ -1577,7 +1629,6 @@ void SqliteStorage::setBufferActivity(UserId user, BufferId bufferId, Message::T
     unlock();
 }
 
-
 QHash<BufferId, Message::Types> SqliteStorage::bufferActivities(UserId user)
 {
     QHash<BufferId, Message::Types> bufferActivityHash;
@@ -1606,13 +1657,12 @@ QHash<BufferId, Message::Types> SqliteStorage::bufferActivities(UserId user)
     return bufferActivityHash;
 }
 
-
 Message::Types SqliteStorage::bufferActivity(BufferId bufferId, MsgId lastSeenMsgId)
 {
     QSqlDatabase db = logDb();
     db.transaction();
 
-    Message::Types result = Message::Types(0);
+    Message::Types result = Message::Types(nullptr);
     {
         QSqlQuery query(db);
         query.prepare(queryString("select_buffer_bufferactivity"));
@@ -1630,7 +1680,7 @@ Message::Types SqliteStorage::bufferActivity(BufferId bufferId, MsgId lastSeenMs
     return result;
 }
 
-QHash<QString, QByteArray> SqliteStorage::bufferCiphers(UserId user, const NetworkId &networkId)
+QHash<QString, QByteArray> SqliteStorage::bufferCiphers(UserId user, const NetworkIdnetworkId)
 {
     QHash<QString, QByteArray> bufferCiphers;
 
@@ -1653,7 +1703,7 @@ QHash<QString, QByteArray> SqliteStorage::bufferCiphers(UserId user, const Netwo
     return bufferCiphers;
 }
 
-void SqliteStorage::setBufferCipher(UserId user, const NetworkId &networkId, const QString &bufferName, const QByteArray &cipher)
+void SqliteStorage::setBufferCipher(UserId user, const NetworkId& networkId, const QString& bufferName, const QByteArray& cipher)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1694,7 +1744,6 @@ void SqliteStorage::setHighlightCount(UserId user, BufferId bufferId, int count)
     unlock();
 }
 
-
 QHash<BufferId, int> SqliteStorage::highlightCounts(UserId user)
 {
     QHash<BufferId, int> highlightCountHash;
@@ -1723,7 +1772,6 @@ QHash<BufferId, int> SqliteStorage::highlightCounts(UserId user)
     return highlightCountHash;
 }
 
-
 int SqliteStorage::highlightCount(BufferId bufferId, MsgId lastSeenMsgId)
 {
     QSqlDatabase db = logDb();
@@ -1747,7 +1795,7 @@ int SqliteStorage::highlightCount(BufferId bufferId, MsgId lastSeenMsgId)
     return result;
 }
 
-bool SqliteStorage::logMessage(Message &msg)
+bool SqliteStorage::logMessage(Messagemsg)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1773,7 +1821,7 @@ bool SqliteStorage::logMessage(Message &msg)
 
         if (logMessageQuery.lastError().isValid()) {
             // constraint violation - must be NOT NULL constraint - probably the sender is missing...
-            if (logMessageQuery.lastError().number() == 19) {
+            if (logMessageQuery.lastError().nativeErrorCode() == QLatin1String{"19"}) {
                 QSqlQuery addSenderQuery(db);
                 addSenderQuery.prepare(queryString("insert_sender"));
                 addSenderQuery.bindValue(":sender", msg.sender());
@@ -1809,8 +1857,7 @@ bool SqliteStorage::logMessage(Message &msg)
     return !error;
 }
 
-
-bool SqliteStorage::logMessages(MessageList &msgs)
+bool SqliteStorage::logMessages(MessageList& msgs)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1821,8 +1868,8 @@ bool SqliteStorage::logMessages(MessageList &msgs)
         addSenderQuery.prepare(queryString("insert_sender"));
         lockForWrite();
         for (int i = 0; i < msgs.count(); i++) {
-            auto &msg = msgs.at(i);
-            SenderData sender = { msg.sender(), msg.realName(), msg.avatarUrl() };
+            automsg = msgs.at(i);
+            SenderData sender = {msg.sender(), msg.realName(), msg.avatarUrl()};
             if (senders.contains(sender))
                 continue;
             senders << sender;
@@ -1839,7 +1886,7 @@ bool SqliteStorage::logMessages(MessageList &msgs)
         QSqlQuery logMessageQuery(db);
         logMessageQuery.prepare(queryString("insert_message"));
         for (int i = 0; i < msgs.count(); i++) {
-            Message &msg = msgs[i];
+            Messagemsg = msgs[i];
             // As of SQLite schema version 31, timestamps are stored in milliseconds instead of
             // seconds.  This nets us more precision as well as simplifying 64-bit time.
             logMessageQuery.bindValue(":time", msg.timestamp().toMSecsSinceEpoch());
@@ -1878,10 +1925,9 @@ bool SqliteStorage::logMessages(MessageList &msgs)
     return !error;
 }
 
-
-QList<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, MsgId first, MsgId last, int limit)
+std::vector<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, MsgId first, MsgId last, int limit)
 {
-    QList<Message> messagelist;
+    std::vector<Message> messagelist;
 
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1900,7 +1946,11 @@ QList<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, MsgId
         safeExec(bufferInfoQuery);
         error = !watchQuery(bufferInfoQuery) || !bufferInfoQuery.first();
         if (!error) {
-            bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(), bufferInfoQuery.value(1).toInt(), (BufferInfo::Type)bufferInfoQuery.value(2).toInt(), 0, bufferInfoQuery.value(4).toString());
+            bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(),
+                                    bufferInfoQuery.value(1).toInt(),
+                                    (BufferInfo::Type)bufferInfoQuery.value(2).toInt(),
+                                    0,
+                                    bufferInfoQuery.value(4).toString());
             error = !bufferInfo.isValid();
         }
     }
@@ -1944,7 +1994,7 @@ QList<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, MsgId
                 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();
@@ -1953,10 +2003,10 @@ QList<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, MsgId
     return messagelist;
 }
 
-
-QList<Message> SqliteStorage::requestMsgsFiltered(UserId user, BufferId bufferId, MsgId first, MsgId last, int limit, Message::Types type, Message::Flags flags)
+std::vector<Message> SqliteStorage::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();
     db.transaction();
@@ -1975,7 +2025,11 @@ QList<Message> SqliteStorage::requestMsgsFiltered(UserId user, BufferId bufferId
         safeExec(bufferInfoQuery);
         error = !watchQuery(bufferInfoQuery) || !bufferInfoQuery.first();
         if (!error) {
-            bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(), bufferInfoQuery.value(1).toInt(), (BufferInfo::Type)bufferInfoQuery.value(2).toInt(), 0, bufferInfoQuery.value(4).toString());
+            bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(),
+                                    bufferInfoQuery.value(1).toInt(),
+                                    (BufferInfo::Type)bufferInfoQuery.value(2).toInt(),
+                                    0,
+                                    bufferInfoQuery.value(4).toString());
             error = !bufferInfo.isValid();
         }
     }
@@ -2011,20 +2065,20 @@ QList<Message> SqliteStorage::requestMsgsFiltered(UserId user, BufferId bufferId
 
         while (query.next()) {
             Message msg(
-                        // As of SQLite schema version 31, timestamps are stored in milliseconds
-                        // instead of seconds.  This nets us more precision as well as simplifying
-                        // 64-bit time.
-                        QDateTime::fromMSecsSinceEpoch(query.value(1).toLongLong()),
-                        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()});
+                // As of SQLite schema version 31, timestamps are stored in milliseconds
+                // instead of seconds.  This nets us more precision as well as simplifying
+                // 64-bit time.
+                QDateTime::fromMSecsSinceEpoch(query.value(1).toLongLong()),
+                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 << msg;
+            messagelist.push_back(std::move(msg));
         }
     }
     db.commit();
@@ -2033,10 +2087,9 @@ QList<Message> SqliteStorage::requestMsgsFiltered(UserId user, BufferId bufferId
     return messagelist;
 }
 
-
-QList<Message> SqliteStorage::requestAllMsgs(UserId user, MsgId first, MsgId last, int limit)
+std::vector<Message> SqliteStorage::requestAllMsgs(UserId user, MsgId first, MsgId last, int limit)
 {
-    QList<Message> messagelist;
+    std::vector<Message> messagelist;
 
     QSqlDatabase db = logDb();
     db.transaction();
@@ -2051,7 +2104,11 @@ QList<Message> SqliteStorage::requestAllMsgs(UserId user, MsgId first, MsgId las
         safeExec(bufferInfoQuery);
         watchQuery(bufferInfoQuery);
         while (bufferInfoQuery.next()) {
-            BufferInfo bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(), bufferInfoQuery.value(1).toInt(), (BufferInfo::Type)bufferInfoQuery.value(2).toInt(), bufferInfoQuery.value(3).toInt(), bufferInfoQuery.value(4).toString());
+            BufferInfo bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(),
+                                               bufferInfoQuery.value(1).toInt(),
+                                               (BufferInfo::Type)bufferInfoQuery.value(2).toInt(),
+                                               bufferInfoQuery.value(3).toInt(),
+                                               bufferInfoQuery.value(4).toString());
             bufferInfoHash[bufferInfo.bufferId()] = bufferInfo;
         }
 
@@ -2084,7 +2141,7 @@ QList<Message> SqliteStorage::requestAllMsgs(UserId user, MsgId first, MsgId las
                 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();
@@ -2092,9 +2149,9 @@ QList<Message> SqliteStorage::requestAllMsgs(UserId user, MsgId first, MsgId las
     return messagelist;
 }
 
-QList<Message> SqliteStorage::requestAllMsgsFiltered(UserId user, MsgId first, MsgId last, int limit, Message::Types type, Message::Flags flags)
+std::vector<Message> SqliteStorage::requestAllMsgsFiltered(UserId user, MsgId first, MsgId last, int limit, Message::Types type, Message::Flags flags)
 {
-    QList<Message> messagelist;
+    std::vector<Message> messagelist;
 
     QSqlDatabase db = logDb();
     db.transaction();
@@ -2109,7 +2166,11 @@ QList<Message> SqliteStorage::requestAllMsgsFiltered(UserId user, MsgId first, M
         safeExec(bufferInfoQuery);
         watchQuery(bufferInfoQuery);
         while (bufferInfoQuery.next()) {
-            BufferInfo bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(), bufferInfoQuery.value(1).toInt(), (BufferInfo::Type)bufferInfoQuery.value(2).toInt(), bufferInfoQuery.value(3).toInt(), bufferInfoQuery.value(4).toString());
+            BufferInfo bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(),
+                                               bufferInfoQuery.value(1).toInt(),
+                                               (BufferInfo::Type)bufferInfoQuery.value(2).toInt(),
+                                               bufferInfoQuery.value(3).toInt(),
+                                               bufferInfoQuery.value(4).toString());
             bufferInfoHash[bufferInfo.bufferId()] = bufferInfo;
         }
 
@@ -2134,20 +2195,20 @@ QList<Message> SqliteStorage::requestAllMsgsFiltered(UserId user, MsgId first, M
 
         while (query.next()) {
             Message msg(
-                        // As of SQLite schema version 31, timestamps are stored in milliseconds
-                        // instead of seconds.  This nets us more precision as well as simplifying
-                        // 64-bit time.
-                        QDateTime::fromMSecsSinceEpoch(query.value(2).toLongLong()),
-                        bufferInfoHash[query.value(1).toInt()],
-                        (Message::Type)query.value(3).toInt(),
-                        query.value(9).toString(),
-                        query.value(5).toString(),
-                        query.value(6).toString(),
-                        query.value(7).toString(),
-                        query.value(8).toString(),
-                        Message::Flags{query.value(4).toInt()});
+                // As of SQLite schema version 31, timestamps are stored in milliseconds
+                // instead of seconds.  This nets us more precision as well as simplifying
+                // 64-bit time.
+                QDateTime::fromMSecsSinceEpoch(query.value(2).toLongLong()),
+                bufferInfoHash[query.value(1).toInt()],
+                (Message::Type)query.value(3).toInt(),
+                query.value(9).toString(),
+                query.value(5).toString(),
+                query.value(6).toString(),
+                query.value(7).toString(),
+                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();
@@ -2177,62 +2238,35 @@ QMap<UserId, QString> SqliteStorage::getAllAuthUserNames()
     return authusernames;
 }
 
-
-QString SqliteStorage::getAuthUserName(UserId user) {
-    QString authusername;
-    QSqlQuery query(logDb());
-    query.prepare(queryString("select_authusername"));
-    query.bindValue(":userid", user.toInt());
-
-    lockForRead();
-    safeExec(query);
-    watchQuery(query);
-    unlock();
-
-    if (query.first()) {
-        authusername = query.value(0).toString();
-    }
-
-    return authusername;
-}
-
-
 QString SqliteStorage::backlogFile()
 {
     return Quassel::configDirPath() + "quassel-storage.sqlite";
 }
 
-
-bool SqliteStorage::safeExec(QSqlQuery &query, int retryCount)
+bool SqliteStorage::safeExec(QSqlQuery& query, int retryCount)
 {
     query.exec();
 
     if (!query.lastError().isValid())
         return true;
 
-    switch (query.lastError().number()) {
-    case 5: // SQLITE_BUSY         5   /* The database file is locked */
-        [[clang::fallthrough]];
-    case 6: // SQLITE_LOCKED       6   /* A table in the database is locked */
+    QString nativeErrorCode = query.lastError().nativeErrorCode();
+
+    // SQLITE_BUSY         5   /* The database file is locked */
+    // SQLITE_LOCKED       6   /* A table in the database is locked */
+    if (nativeErrorCode == QLatin1String{"5"} || nativeErrorCode == QLatin1String{"6"}) {
         if (retryCount < _maxRetryCount)
             return safeExec(query, retryCount + 1);
-        break;
-    default:
-        ;
     }
     return false;
 }
 
-
 // ========================================
 //  SqliteMigration
 // ========================================
 SqliteMigrationReader::SqliteMigrationReader()
-    : SqliteStorage(),
-    _maxId(0)
-{
-}
-
+    : SqliteStorage()
+{}
 
 void SqliteMigrationReader::setMaxId(MigrationObject mo)
 {
@@ -2253,7 +2287,6 @@ void SqliteMigrationReader::setMaxId(MigrationObject mo)
     _maxId = query.value(0).toLongLong();
 }
 
-
 bool SqliteMigrationReader::prepareQuery(MigrationObject mo)
 {
     setMaxId(mo);
@@ -2297,8 +2330,7 @@ bool SqliteMigrationReader::prepareQuery(MigrationObject mo)
     return exec();
 }
 
-
-bool SqliteMigrationReader::readMo(QuasselUserMO &user)
+bool SqliteMigrationReader::readMo(QuasselUserMO& user)
 {
     if (!next())
         return false;
@@ -2311,8 +2343,7 @@ bool SqliteMigrationReader::readMo(QuasselUserMO &user)
     return true;
 }
 
-
-bool SqliteMigrationReader::readMo(IdentityMO &identity)
+bool SqliteMigrationReader::readMo(IdentityMO& identity)
 {
     if (!next())
         return false;
@@ -2341,8 +2372,7 @@ bool SqliteMigrationReader::readMo(IdentityMO &identity)
     return true;
 }
 
-
-bool SqliteMigrationReader::readMo(IdentityNickMO &identityNick)
+bool SqliteMigrationReader::readMo(IdentityNickMO& identityNick)
 {
     if (!next())
         return false;
@@ -2353,8 +2383,7 @@ bool SqliteMigrationReader::readMo(IdentityNickMO &identityNick)
     return true;
 }
 
-
-bool SqliteMigrationReader::readMo(NetworkMO &network)
+bool SqliteMigrationReader::readMo(NetworkMO& network)
 {
     if (!next())
         return false;
@@ -2392,8 +2421,7 @@ bool SqliteMigrationReader::readMo(NetworkMO &network)
     return true;
 }
 
-
-bool SqliteMigrationReader::readMo(BufferMO &buffer)
+bool SqliteMigrationReader::readMo(BufferMO& buffer)
 {
     if (!next())
         return false;
@@ -2416,8 +2444,7 @@ bool SqliteMigrationReader::readMo(BufferMO &buffer)
     return true;
 }
 
-
-bool SqliteMigrationReader::readMo(SenderMO &sender)
+bool SqliteMigrationReader::readMo(SenderMO& sender)
 {
     int skipSteps = 0;
     while (!next()) {
@@ -2440,8 +2467,7 @@ bool SqliteMigrationReader::readMo(SenderMO &sender)
     return true;
 }
 
-
-bool SqliteMigrationReader::readMo(BacklogMO &backlog)
+bool SqliteMigrationReader::readMo(BacklogMO& backlog)
 {
     qint64 skipSteps = 0;
     while (!next()) {
@@ -2470,8 +2496,7 @@ bool SqliteMigrationReader::readMo(BacklogMO &backlog)
     return true;
 }
 
-
-bool SqliteMigrationReader::readMo(IrcServerMO &ircserver)
+bool SqliteMigrationReader::readMo(IrcServerMO& ircserver)
 {
     if (!next())
         return false;
@@ -2494,8 +2519,7 @@ bool SqliteMigrationReader::readMo(IrcServerMO &ircserver)
     return true;
 }
 
-
-bool SqliteMigrationReader::readMo(UserSettingMO &userSetting)
+bool SqliteMigrationReader::readMo(UserSettingMO& userSetting)
 {
     if (!next())
         return false;
@@ -2507,8 +2531,7 @@ bool SqliteMigrationReader::readMo(UserSettingMO &userSetting)
     return true;
 }
 
-
-bool SqliteMigrationReader::readMo(CoreStateMO &coreState)
+bool SqliteMigrationReader::readMo(CoreStateMO& coreState)
 {
     if (!next())
         return false;