/***************************************************************************
- * 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"
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;
}
- 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 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)
{
QSqlDatabase db = logDb();
lockForWrite();
safeExec(query);
if (query.lastError().isValid()
- && query.lastError().number() == 19) { // user already exists - sadly 19 seems to be the general constraint violation error...
+ && query.lastError().nativeErrorCode() == QLatin1String{"19"}) { // user already exists - sadly 19 seems to be the general constraint violation error...
db.rollback();
}
else {
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();
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();
}
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();
servers << server;
}
net.serverList = servers;
- nets << net;
+ nets.push_back(std::move(net));
}
}
}
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();
watchQuery(query);
while (query.next()) {
- connectedNets << query.value(0).toInt();
+ connectedNets.emplace_back(query.value(0).toInt());
}
db.commit();
}
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();
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> 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();
safeExec(query);
watchQuery(query);
while (query.next()) {
- bufferList << BufferId(query.value(0).toInt());
+ bufferList.emplace_back(query.value(0).toInt());
}
db.commit();
}
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 {
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)
{
QSqlDatabase db = logDb();
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());
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();
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> SqliteStorage::requestMsgsFiltered(
+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();
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> 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();
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> 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();
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();
if (!query.lastError().isValid())
return true;
- switch (query.lastError().number()) {
- case 5: // SQLITE_BUSY 5 /* The database file is locked */
- // 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;
}