/***************************************************************************
- * Copyright (C) 2005-2016 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 "logger.h"
#include "network.h"
#include "quassel.h"
int SqliteStorage::_maxRetryCount = 150;
-SqliteStorage::SqliteStorage(QObject *parent)
+SqliteStorage::SqliteStorage(QObject* parent)
: 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
// We identify the backend to use for the monolithic core by its displayname.
// so only change this string if you _really_ have to and make sure the core
// setup for the mono client still works ;)
- return QString("SQLite");
+ 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 "
"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)
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)
{
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)
+UserId SqliteStorage::addUser(const QString& user, const QString& password, const QString& authenticator)
{
QSqlDatabase db = logDb();
UserId uid;
query.bindValue(":username", user);
query.bindValue(":password", hashPassword(password));
query.bindValue(":hashversion", Storage::HashVersion::Latest);
+ 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 {
return uid;
}
-
-bool SqliteStorage::updateUser(UserId user, const QString &password)
+bool SqliteStorage::updateUser(UserId user, const QString& password)
{
QSqlDatabase db = logDb();
bool success = false;
return success;
}
-
-void SqliteStorage::renameUser(UserId user, const QString &newName)
+void SqliteStorage::renameUser(UserId user, const QString& newName)
{
QSqlDatabase db = logDb();
db.transaction();
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;
return returnUserId;
}
-
-UserId SqliteStorage::getUserId(const QString &username)
+UserId SqliteStorage::getUserId(const QString& username)
{
UserId userId;
return userId;
}
+QString SqliteStorage::getUserAuthenticator(const UserId userid)
+{
+ QString authenticator = QString("");
+
+ {
+ QSqlQuery query(logDb());
+ query.prepare(queryString("select_authenticator"));
+ query.bindValue(":userid", userid.toInt());
+
+ lockForRead();
+ safeExec(query);
+
+ if (query.first()) {
+ authenticator = query.value(0).toString();
+ }
+ }
+ unlock();
+
+ return authenticator;
+}
UserId SqliteStorage::internalUser()
{
return userId;
}
-
void SqliteStorage::delUser(UserId user)
{
QSqlDatabase db = logDb();
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);
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;
{
return data;
}
+void SqliteStorage::setCoreState(const QVariantList& data)
+{
+ QByteArray rawData;
+ QDataStream out(&rawData, QIODevice::WriteOnly);
+ out.setVersion(QDataStream::Qt_4_2);
+ out << data;
+
+ QSqlDatabase db = logDb();
+ db.transaction();
+ {
+ QSqlQuery query(db);
+ query.prepare(queryString("insert_core_state"));
+ query.bindValue(":key", "active_sessions");
+ query.bindValue(":value", rawData);
+ lockForWrite();
+ safeExec(query);
+
+ if (query.lastError().isValid()) {
+ QSqlQuery updateQuery(db);
+ updateQuery.prepare(queryString("update_core_state"));
+ updateQuery.bindValue(":key", "active_sessions");
+ updateQuery.bindValue(":value", rawData);
+ safeExec(updateQuery);
+ }
+ db.commit();
+ }
+ unlock();
+}
+
+QVariantList SqliteStorage::getCoreState(const QVariantList& defaultData)
+{
+ QVariantList data;
+ {
+ QSqlQuery query(logDb());
+ query.prepare(queryString("select_core_state"));
+ query.bindValue(":key", "active_sessions");
+ lockForRead();
+ safeExec(query);
+
+ if (query.first()) {
+ QByteArray rawData = query.value(0).toByteArray();
+ QDataStream in(&rawData, QIODevice::ReadOnly);
+ in.setVersion(QDataStream::Qt_4_2);
+ in >> data;
+ }
+ else {
+ data = defaultData;
+ }
+ }
+ unlock();
+ return data;
+}
-IdentityId SqliteStorage::createIdentity(UserId user, CoreIdentity &identity)
+IdentityId SqliteStorage::createIdentity(UserId user, CoreIdentity& identity)
{
IdentityId identityId;
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
lockForWrite();
safeExec(query);
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);
return identityId;
}
-
-bool SqliteStorage::updateIdentity(UserId user, const CoreIdentity &identity)
+bool SqliteStorage::updateIdentity(UserId user, const CoreIdentity& identity)
{
QSqlDatabase db = logDb();
bool error = false;
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);
watchQuery(query);
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);
return true;
}
-
void SqliteStorage::removeIdentity(UserId user, IdentityId identityId)
{
QSqlDatabase db = logDb();
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();
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<QString> nicks;
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 identities;
}
-
-NetworkId SqliteStorage::createNetwork(UserId user, const NetworkInfo &info)
+NetworkId SqliteStorage::createNetwork(UserId user, const NetworkInfo& info)
{
NetworkId networkId;
}
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);
}
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());
query.bindValue(":messagerateburstsize", info.messageRateBurstSize);
query.bindValue(":messageratedelay", info.messageRateDelay);
query.bindValue(":unlimitedmessagerate", info.unlimitedMessageRate ? 1 : 0);
+ query.bindValue(":skipcaps", info.skipCapsToString());
if (info.networkId.isValid())
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);
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;
{
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);
return !error;
}
-
-bool SqliteStorage::removeNetwork(UserId user, const NetworkId &networkId)
+bool SqliteStorage::removeNetwork(UserId user, const NetworkId& networkId)
{
QSqlDatabase db = logDb();
bool error = false;
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();
net.messageRateBurstSize = networksQuery.value(20).toUInt();
net.messageRateDelay = networksQuery.value(21).toUInt();
net.unlimitedMessageRate = networksQuery.value(22).toInt() == 1 ? true : false;
+ net.skipCapsFromString(networksQuery.value(23).toString());
serversQuery.bindValue(":networkid", net.networkId.toInt());
safeExec(serversQuery);
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 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();
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;
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();
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();
unlock();
}
-
QString SqliteStorage::awayMessage(UserId user, NetworkId networkId)
{
QSqlDatabase db = logDb();
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();
unlock();
}
-
QString SqliteStorage::userModes(UserId user, NetworkId networkId)
{
QSqlDatabase db = logDb();
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();
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();
return bufferInfo;
}
-
-BufferInfo SqliteStorage::getBufferInfo(UserId user, const BufferId &bufferId)
+BufferInfo SqliteStorage::getBufferInfo(UserId user, const BufferId& bufferId)
{
QSqlDatabase db = logDb();
db.transaction();
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();
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();
}
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();
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();
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;
}
-
-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();
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();
query.prepare(queryString("update_buffer_lastseen"));
query.bindValue(":userid", user.toInt());
query.bindValue(":bufferid", bufferId.toInt());
- query.bindValue(":lastseenmsgid", msgId.toInt());
+ query.bindValue(":lastseenmsgid", msgId.toQint64());
lockForWrite();
safeExec(query);
unlock();
}
-
QHash<BufferId, MsgId> SqliteStorage::bufferLastSeenMsgIds(UserId user)
{
QHash<BufferId, MsgId> lastSeenHash;
error = !watchQuery(query);
if (!error) {
while (query.next()) {
- lastSeenHash[query.value(0).toInt()] = query.value(1).toInt();
+ lastSeenHash[query.value(0).toInt()] = query.value(1).toLongLong();
}
}
}
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();
query.prepare(queryString("update_buffer_markerlinemsgid"));
query.bindValue(":userid", user.toInt());
query.bindValue(":bufferid", bufferId.toInt());
- query.bindValue(":markerlinemsgid", msgId.toInt());
+ query.bindValue(":markerlinemsgid", msgId.toQint64());
lockForWrite();
safeExec(query);
unlock();
}
-
QHash<BufferId, MsgId> SqliteStorage::bufferMarkerLineMsgIds(UserId user)
{
QHash<BufferId, MsgId> markerLineHash;
error = !watchQuery(query);
if (!error) {
while (query.next()) {
- markerLineHash[query.value(0).toInt()] = query.value(1).toInt();
+ markerLineHash[query.value(0).toInt()] = query.value(1).toLongLong();
}
}
}
return markerLineHash;
}
-void SqliteStorage::setBufferLastMsg(const BufferId &bufferId, const MsgId &msgId)
+void SqliteStorage::setBufferActivity(UserId user, BufferId bufferId, Message::Types bufferActivity)
{
- QSqlQuery query(logDb());
- query.prepare(queryString("update_buffer_lastmsgid"));
+ QSqlDatabase db = logDb();
+ db.transaction();
- query.bindValue(":bufferid", bufferId.toInt());
- query.bindValue(":lastmsgid", msgId.toInt());
- safeExec(query);
- watchQuery(query);
+ {
+ QSqlQuery query(db);
+ query.prepare(queryString("update_buffer_bufferactivity"));
+ query.bindValue(":userid", user.toInt());
+ query.bindValue(":bufferid", bufferId.toInt());
+ query.bindValue(":bufferactivity", (int)bufferActivity);
+
+ lockForWrite();
+ safeExec(query);
+ watchQuery(query);
+ }
+ db.commit();
+ unlock();
}
-bool SqliteStorage::logMessage(Message &msg)
+QHash<BufferId, Message::Types> SqliteStorage::bufferActivities(UserId user)
{
+ QHash<BufferId, Message::Types> bufferActivityHash;
+
QSqlDatabase db = logDb();
db.transaction();
bool error = false;
{
- QSqlQuery logMessageQuery(db);
- logMessageQuery.prepare(queryString("insert_message"));
-
- logMessageQuery.bindValue(":time", msg.timestamp().toTime_t());
- logMessageQuery.bindValue(":bufferid", msg.bufferInfo().bufferId().toInt());
- logMessageQuery.bindValue(":type", msg.type());
- logMessageQuery.bindValue(":flags", (int)msg.flags());
- logMessageQuery.bindValue(":sender", msg.sender());
- logMessageQuery.bindValue(":message", msg.contents());
-
- lockForWrite();
- safeExec(logMessageQuery);
+ QSqlQuery query(db);
+ query.prepare(queryString("select_buffer_bufferactivities"));
+ query.bindValue(":userid", user.toInt());
- if (logMessageQuery.lastError().isValid()) {
- // constraint violation - must be NOT NULL constraint - probably the sender is missing...
- if (logMessageQuery.lastError().number() == 19) {
- QSqlQuery addSenderQuery(db);
- addSenderQuery.prepare(queryString("insert_sender"));
- addSenderQuery.bindValue(":sender", msg.sender());
- safeExec(addSenderQuery);
- safeExec(logMessageQuery);
- error = !watchQuery(logMessageQuery);
- }
- else {
- watchQuery(logMessageQuery);
- }
- }
+ lockForRead();
+ safeExec(query);
+ error = !watchQuery(query);
if (!error) {
- MsgId msgId = logMessageQuery.lastInsertId().toInt();
- if (msgId.isValid()) {
- msg.setMsgId(msgId);
-
- setBufferLastMsg(msg.bufferInfo().bufferId(), msgId);
- }
- else {
- error = true;
+ while (query.next()) {
+ bufferActivityHash[query.value(0).toInt()] = Message::Types(query.value(1).toInt());
}
}
}
- if (error) {
- db.rollback();
- }
- else {
- db.commit();
- }
-
+ db.commit();
unlock();
- return !error;
+ return bufferActivityHash;
}
-
-bool SqliteStorage::logMessages(MessageList &msgs)
+Message::Types SqliteStorage::bufferActivity(BufferId bufferId, MsgId lastSeenMsgId)
{
QSqlDatabase db = logDb();
db.transaction();
+ Message::Types result = Message::Types(nullptr);
{
- QSet<QString> senders;
- QSqlQuery addSenderQuery(db);
- addSenderQuery.prepare(queryString("insert_sender"));
- lockForWrite();
- for (int i = 0; i < msgs.count(); i++) {
- const QString &sender = msgs.at(i).sender();
- if (senders.contains(sender))
- continue;
- senders << sender;
+ QSqlQuery query(db);
+ query.prepare(queryString("select_buffer_bufferactivity"));
+ query.bindValue(":bufferid", bufferId.toInt());
+ query.bindValue(":lastseenmsgid", lastSeenMsgId.toQint64());
- addSenderQuery.bindValue(":sender", sender);
- safeExec(addSenderQuery);
- }
+ lockForRead();
+ safeExec(query);
+ if (query.first())
+ result = Message::Types(query.value(0).toInt());
}
- bool error = false;
- {
- QSqlQuery logMessageQuery(db);
- logMessageQuery.prepare(queryString("insert_message"));
- for (int i = 0; i < msgs.count(); i++) {
- Message &msg = msgs[i];
-
- logMessageQuery.bindValue(":time", msg.timestamp().toTime_t());
- logMessageQuery.bindValue(":bufferid", msg.bufferInfo().bufferId().toInt());
+ db.commit();
+ unlock();
+ return result;
+}
+
+QHash<QString, QByteArray> SqliteStorage::bufferCiphers(UserId user, const NetworkId& networkId)
+{
+ QHash<QString, QByteArray> bufferCiphers;
+
+ QSqlDatabase db = logDb();
+ db.transaction();
+ {
+ QSqlQuery query(db);
+ query.prepare(queryString("select_buffer_ciphers"));
+ query.bindValue(":userid", user.toInt());
+ query.bindValue(":networkid", networkId.toInt());
+
+ lockForRead();
+ safeExec(query);
+ watchQuery(query);
+ while (query.next()) {
+ bufferCiphers[query.value(0).toString()] = QByteArray::fromHex(query.value(1).toString().toUtf8());
+ }
+ }
+ unlock();
+ return bufferCiphers;
+}
+
+void SqliteStorage::setBufferCipher(UserId user, const NetworkId& networkId, const QString& bufferName, const QByteArray& cipher)
+{
+ QSqlDatabase db = logDb();
+ db.transaction();
+
+ {
+ QSqlQuery query(db);
+ query.prepare(queryString("update_buffer_cipher"));
+ query.bindValue(":userid", user.toInt());
+ query.bindValue(":networkid", networkId.toInt());
+ query.bindValue(":buffercname", bufferName.toLower());
+ query.bindValue(":cipher", QString(cipher.toHex()));
+
+ lockForWrite();
+ safeExec(query);
+ watchQuery(query);
+ db.commit();
+ }
+ unlock();
+}
+
+void SqliteStorage::setHighlightCount(UserId user, BufferId bufferId, int count)
+{
+ QSqlDatabase db = logDb();
+ db.transaction();
+
+ {
+ QSqlQuery query(db);
+ query.prepare(queryString("update_buffer_highlightcount"));
+ query.bindValue(":userid", user.toInt());
+ query.bindValue(":bufferid", bufferId.toInt());
+ query.bindValue(":highlightcount", count);
+
+ lockForWrite();
+ safeExec(query);
+ watchQuery(query);
+ }
+ db.commit();
+ unlock();
+}
+
+QHash<BufferId, int> SqliteStorage::highlightCounts(UserId user)
+{
+ QHash<BufferId, int> highlightCountHash;
+
+ QSqlDatabase db = logDb();
+ db.transaction();
+
+ bool error = false;
+ {
+ QSqlQuery query(db);
+ query.prepare(queryString("select_buffer_highlightcounts"));
+ query.bindValue(":userid", user.toInt());
+
+ lockForRead();
+ safeExec(query);
+ error = !watchQuery(query);
+ if (!error) {
+ while (query.next()) {
+ highlightCountHash[query.value(0).toInt()] = query.value(1).toInt();
+ }
+ }
+ }
+
+ db.commit();
+ unlock();
+ return highlightCountHash;
+}
+
+int SqliteStorage::highlightCount(BufferId bufferId, MsgId lastSeenMsgId)
+{
+ QSqlDatabase db = logDb();
+ db.transaction();
+
+ int result = 0;
+ {
+ QSqlQuery query(db);
+ query.prepare(queryString("select_buffer_highlightcount"));
+ query.bindValue(":bufferid", bufferId.toInt());
+ query.bindValue(":lastseenmsgid", lastSeenMsgId.toQint64());
+
+ lockForRead();
+ safeExec(query);
+ if (query.first())
+ result = query.value(0).toInt();
+ }
+
+ db.commit();
+ unlock();
+ return result;
+}
+
+bool SqliteStorage::logMessage(Message& msg)
+{
+ QSqlDatabase db = logDb();
+ db.transaction();
+
+ bool error = false;
+ {
+ QSqlQuery logMessageQuery(db);
+ logMessageQuery.prepare(queryString("insert_message"));
+ // 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());
+ logMessageQuery.bindValue(":bufferid", msg.bufferInfo().bufferId().toInt());
+ logMessageQuery.bindValue(":type", msg.type());
+ logMessageQuery.bindValue(":flags", (int)msg.flags());
+ logMessageQuery.bindValue(":sender", msg.sender());
+ logMessageQuery.bindValue(":realname", msg.realName());
+ logMessageQuery.bindValue(":avatarurl", msg.avatarUrl());
+ logMessageQuery.bindValue(":senderprefixes", msg.senderPrefixes());
+ logMessageQuery.bindValue(":message", msg.contents());
+
+ lockForWrite();
+ safeExec(logMessageQuery);
+
+ if (logMessageQuery.lastError().isValid()) {
+ // constraint violation - must be NOT NULL constraint - probably the sender is missing...
+ if (logMessageQuery.lastError().nativeErrorCode() == QLatin1String{"19"}) {
+ QSqlQuery addSenderQuery(db);
+ addSenderQuery.prepare(queryString("insert_sender"));
+ addSenderQuery.bindValue(":sender", msg.sender());
+ addSenderQuery.bindValue(":realname", msg.realName());
+ addSenderQuery.bindValue(":avatarurl", msg.avatarUrl());
+ safeExec(addSenderQuery);
+ safeExec(logMessageQuery);
+ error = !watchQuery(logMessageQuery);
+ }
+ else {
+ watchQuery(logMessageQuery);
+ }
+ }
+ if (!error) {
+ MsgId msgId = logMessageQuery.lastInsertId().toLongLong();
+ if (msgId.isValid()) {
+ msg.setMsgId(msgId);
+ }
+ else {
+ error = true;
+ }
+ }
+ }
+
+ if (error) {
+ db.rollback();
+ }
+ else {
+ db.commit();
+ }
+
+ unlock();
+ return !error;
+}
+
+bool SqliteStorage::logMessages(MessageList& msgs)
+{
+ QSqlDatabase db = logDb();
+ db.transaction();
+
+ {
+ QSet<SenderData> senders;
+ QSqlQuery addSenderQuery(db);
+ 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()};
+ if (senders.contains(sender))
+ continue;
+ senders << sender;
+
+ addSenderQuery.bindValue(":sender", sender.sender);
+ addSenderQuery.bindValue(":realname", sender.realname);
+ addSenderQuery.bindValue(":avatarurl", sender.avatarurl);
+ safeExec(addSenderQuery);
+ }
+ }
+
+ bool error = false;
+ {
+ QSqlQuery logMessageQuery(db);
+ logMessageQuery.prepare(queryString("insert_message"));
+ for (int i = 0; i < msgs.count(); i++) {
+ Message& msg = 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());
+ logMessageQuery.bindValue(":bufferid", msg.bufferInfo().bufferId().toInt());
logMessageQuery.bindValue(":type", msg.type());
logMessageQuery.bindValue(":flags", (int)msg.flags());
logMessageQuery.bindValue(":sender", msg.sender());
+ logMessageQuery.bindValue(":realname", msg.realName());
+ logMessageQuery.bindValue(":avatarurl", msg.avatarUrl());
+ logMessageQuery.bindValue(":senderprefixes", msg.senderPrefixes());
logMessageQuery.bindValue(":message", msg.contents());
safeExec(logMessageQuery);
break;
}
else {
- msg.setMsgId(logMessageQuery.lastInsertId().toInt());
+ msg.setMsgId(logMessageQuery.lastInsertId().toLongLong());
}
}
}
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();
bool error = false;
BufferInfo bufferInfo;
{
- // code dupication from getBufferInfo:
+ // code duplication from getBufferInfo:
// this is due to the impossibility of nesting transactions and recursive locking
QSqlQuery bufferInfoQuery(db);
bufferInfoQuery.prepare(queryString("select_buffer_by_id"));
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();
}
}
}
else if (last == -1) {
query.prepare(queryString("select_messagesNewerThan"));
- query.bindValue(":firstmsg", first.toInt());
+ query.bindValue(":firstmsg", first.toQint64());
}
else {
- query.prepare(queryString("select_messages"));
- query.bindValue(":lastmsg", last.toInt());
- query.bindValue(":firstmsg", first.toInt());
+ query.prepare(queryString("select_messagesRange"));
+ query.bindValue(":lastmsg", last.toQint64());
+ query.bindValue(":firstmsg", first.toQint64());
}
query.bindValue(":bufferid", bufferId.toInt());
query.bindValue(":limit", limit);
watchQuery(query);
while (query.next()) {
- Message msg(QDateTime::fromTime_t(query.value(1).toInt()),
+ 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).toUInt(),
+ (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();
+ unlock();
+
+ return messagelist;
+}
+
+std::vector<Message> SqliteStorage::requestMsgsFiltered(
+ UserId user, BufferId bufferId, MsgId first, MsgId last, int limit, Message::Types type, Message::Flags flags)
+{
+ std::vector<Message> messagelist;
+
+ QSqlDatabase db = logDb();
+ db.transaction();
+
+ bool error = false;
+ BufferInfo bufferInfo;
+ {
+ // code dupication from getBufferInfo:
+ // this is due to the impossibility of nesting transactions and recursive locking
+ QSqlQuery bufferInfoQuery(db);
+ bufferInfoQuery.prepare(queryString("select_buffer_by_id"));
+ bufferInfoQuery.bindValue(":userid", user.toInt());
+ bufferInfoQuery.bindValue(":bufferid", bufferId.toInt());
+
+ lockForRead();
+ 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());
+ error = !bufferInfo.isValid();
+ }
+ }
+ if (error) {
+ db.rollback();
+ unlock();
+ return messagelist;
+ }
+
+ {
+ QSqlQuery query(db);
+ if (last == -1 && first == -1) {
+ query.prepare(queryString("select_messagesNewestK_filtered"));
+ }
+ else if (last == -1) {
+ query.prepare(queryString("select_messagesNewerThan_filtered"));
+ query.bindValue(":firstmsg", first.toQint64());
+ }
+ else {
+ query.prepare(queryString("select_messagesRange_filtered"));
+ query.bindValue(":lastmsg", last.toQint64());
+ query.bindValue(":firstmsg", first.toQint64());
+ }
+ query.bindValue(":bufferid", bufferId.toInt());
+ query.bindValue(":limit", limit);
+ int typeRaw = type;
+ query.bindValue(":type", typeRaw);
+ int flagsRaw = flags;
+ query.bindValue(":flags", flagsRaw);
+
+ safeExec(query);
+ watchQuery(query);
+
+ 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(),
- (Message::Flags)query.value(3).toUInt());
- msg.setMsgId(query.value(0).toInt());
- messagelist << msg;
+ 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;
}
+std::vector<Message> SqliteStorage::requestMsgsForward(
+ UserId user, BufferId bufferId, MsgId first, MsgId last, int limit, Message::Types type, Message::Flags flags)
+{
+ std::vector<Message> messagelist;
+
+ QSqlDatabase db = logDb();
+ db.transaction();
+
+ bool error = false;
+ BufferInfo bufferInfo;
+ {
+ // code dupication from getBufferInfo:
+ // this is due to the impossibility of nesting transactions and recursive locking
+ QSqlQuery bufferInfoQuery(db);
+ bufferInfoQuery.prepare(queryString("select_buffer_by_id"));
+ bufferInfoQuery.bindValue(":userid", user.toInt());
+ bufferInfoQuery.bindValue(":bufferid", bufferId.toInt());
+
+ lockForRead();
+ 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());
+ error = !bufferInfo.isValid();
+ }
+ }
+ if (error) {
+ db.rollback();
+ unlock();
+ return messagelist;
+ }
+
+ {
+ QSqlQuery query(db);
+ query.prepare(queryString("select_messagesForward"));
+
+ if (first == -1) {
+ query.bindValue(":firstmsg", std::numeric_limits<qint64>::min());
+ } else {
+ query.bindValue(":firstmsg", first.toQint64());
+ }
+
+ if (last == -1) {
+ query.bindValue(":lastmsg", std::numeric_limits<qint64>::max());
+ } else {
+ query.bindValue(":lastmsg", last.toQint64());
+ }
+
+ query.bindValue(":bufferid", bufferId.toInt());
+
+ int typeRaw = type;
+ int flagsRaw = flags;
+ query.bindValue(":type", typeRaw);
+ query.bindValue(":flags", flagsRaw);
+
+ query.bindValue(":limit", limit);
+
+ safeExec(query);
+ watchQuery(query);
+
+ 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()});
+ msg.setMsgId(query.value(0).toLongLong());
+ messagelist.push_back(std::move(msg));
+ }
+ }
+ db.commit();
+ unlock();
+
+ 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();
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;
}
}
else {
query.prepare(queryString("select_messagesAll"));
- query.bindValue(":lastmsg", last.toInt());
+ query.bindValue(":lastmsg", last.toQint64());
}
query.bindValue(":userid", user.toInt());
- query.bindValue(":firstmsg", first.toInt());
+ query.bindValue(":firstmsg", first.toQint64());
query.bindValue(":limit", limit);
safeExec(query);
watchQuery(query);
while (query.next()) {
- Message msg(QDateTime::fromTime_t(query.value(2).toInt()),
+ 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).toUInt(),
+ (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.push_back(std::move(msg));
+ }
+ }
+ db.commit();
+ unlock();
+ return messagelist;
+}
+
+std::vector<Message> SqliteStorage::requestAllMsgsFiltered(UserId user, MsgId first, MsgId last, int limit, Message::Types type, Message::Flags flags)
+{
+ std::vector<Message> messagelist;
+
+ QSqlDatabase db = logDb();
+ db.transaction();
+
+ QHash<BufferId, BufferInfo> bufferInfoHash;
+ {
+ QSqlQuery bufferInfoQuery(db);
+ bufferInfoQuery.prepare(queryString("select_buffers"));
+ bufferInfoQuery.bindValue(":userid", user.toInt());
+
+ lockForRead();
+ 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());
+ bufferInfoHash[bufferInfo.bufferId()] = bufferInfo;
+ }
+
+ QSqlQuery query(db);
+ if (last == -1) {
+ query.prepare(queryString("select_messagesAllNew_filtered"));
+ }
+ else {
+ query.prepare(queryString("select_messagesAll_filtered"));
+ query.bindValue(":lastmsg", last.toQint64());
+ }
+ query.bindValue(":userid", user.toInt());
+ query.bindValue(":firstmsg", first.toQint64());
+ query.bindValue(":limit", limit);
+ int typeRaw = type;
+ query.bindValue(":type", typeRaw);
+ int flagsRaw = flags;
+ query.bindValue(":flags", flagsRaw);
+ safeExec(query);
+
+ watchQuery(query);
+
+ 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(),
- (Message::Flags)query.value(4).toUInt());
- msg.setMsgId(query.value(0).toInt());
- messagelist << msg;
+ 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.push_back(std::move(msg));
}
}
db.commit();
return messagelist;
}
+QMap<UserId, QString> SqliteStorage::getAllAuthUserNames()
+{
+ QMap<UserId, QString> authusernames;
+
+ QSqlDatabase db = logDb();
+ db.transaction();
+ {
+ QSqlQuery query(db);
+ query.prepare(queryString("select_all_authusernames"));
+
+ lockForRead();
+ safeExec(query);
+ watchQuery(query);
+ while (query.next()) {
+ authusernames[query.value(0).toInt()] = query.value(1).toString();
+ }
+ }
+ db.commit();
+ unlock();
+ return authusernames;
+}
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 */
- 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);
- default:
- return false;
}
+ return false;
}
-
// ========================================
// SqliteMigration
// ========================================
SqliteMigrationReader::SqliteMigrationReader()
- : SqliteStorage(),
- _maxId(0)
-{
-}
-
+ : SqliteStorage()
+{}
void SqliteMigrationReader::setMaxId(MigrationObject mo)
{
}
QSqlQuery query = logDb().exec(queryString);
query.first();
- _maxId = query.value(0).toInt();
+ _maxId = query.value(0).toLongLong();
}
-
bool SqliteMigrationReader::prepareQuery(MigrationObject mo)
{
setMaxId(mo);
case UserSetting:
newQuery(queryString("migrate_read_usersetting"), logDb());
break;
+ case CoreState:
+ newQuery(queryString("migrate_read_corestate"), logDb());
+ break;
}
return exec();
}
-
-bool SqliteMigrationReader::readMo(QuasselUserMO &user)
+bool SqliteMigrationReader::readMo(QuasselUserMO& user)
{
if (!next())
return false;
user.username = value(1).toString();
user.password = value(2).toString();
user.hashversion = value(3).toInt();
+ user.authenticator = value(4).toString();
return true;
}
-
-bool SqliteMigrationReader::readMo(IdentityMO &identity)
+bool SqliteMigrationReader::readMo(IdentityMO& identity)
{
if (!next())
return false;
identity.autoAwayReasonEnabled = value(11).toInt() == 1 ? true : false;
identity.detachAwayEnabled = value(12).toInt() == 1 ? true : false;
identity.detachAwayReason = value(13).toString();
- identity.detchAwayReasonEnabled = value(14).toInt() == 1 ? true : false;
+ identity.detachAwayReasonEnabled = value(14).toInt() == 1 ? true : false;
identity.ident = value(15).toString();
identity.kickReason = value(16).toString();
identity.partReason = value(17).toString();
return true;
}
-
-bool SqliteMigrationReader::readMo(IdentityNickMO &identityNick)
+bool SqliteMigrationReader::readMo(IdentityNickMO& identityNick)
{
if (!next())
return false;
return true;
}
-
-bool SqliteMigrationReader::readMo(NetworkMO &network)
+bool SqliteMigrationReader::readMo(NetworkMO& network)
{
if (!next())
return false;
network.messagerateburstsize = value(26).toInt();
network.messageratedelay = value(27).toUInt();
network.unlimitedmessagerate = value(28).toInt() == 1 ? true : false;
+ // Skipped IRCv3 caps
+ network.skipcaps = value(29).toString();
return true;
}
-
-bool SqliteMigrationReader::readMo(BufferMO &buffer)
+bool SqliteMigrationReader::readMo(BufferMO& buffer)
{
if (!next())
return false;
buffer.buffername = value(4).toString();
buffer.buffercname = value(5).toString();
buffer.buffertype = value(6).toInt();
- buffer.lastseenmsgid = value(7).toInt();
- buffer.markerlinemsgid = value(8).toInt();
- buffer.key = value(9).toString();
- buffer.joined = value(10).toInt() == 1 ? true : false;
+ buffer.lastmsgid = value(7).toLongLong();
+ buffer.lastseenmsgid = value(8).toLongLong();
+ buffer.markerlinemsgid = value(9).toLongLong();
+ buffer.bufferactivity = value(10).toInt();
+ buffer.highlightcount = value(11).toInt();
+ buffer.key = value(12).toString();
+ buffer.joined = value(13).toInt() == 1 ? true : false;
+ buffer.cipher = value(14).toString();
return true;
}
-
-bool SqliteMigrationReader::readMo(SenderMO &sender)
+bool SqliteMigrationReader::readMo(SenderMO& sender)
{
int skipSteps = 0;
while (!next()) {
}
}
- sender.senderId = value(0).toInt();
+ sender.senderId = value(0).toLongLong();
sender.sender = value(1).toString();
+ sender.realname = value(2).toString();
+ sender.avatarurl = value(3).toString();
return true;
}
-
-bool SqliteMigrationReader::readMo(BacklogMO &backlog)
+bool SqliteMigrationReader::readMo(BacklogMO& backlog)
{
- int skipSteps = 0;
+ qint64 skipSteps = 0;
while (!next()) {
if (backlog.messageid < _maxId) {
- bindValue(0, backlog.messageid.toInt() + (skipSteps * stepSize()));
- bindValue(1, backlog.messageid.toInt() + ((skipSteps + 1) * stepSize()));
+ bindValue(0, backlog.messageid.toQint64() + (skipSteps * stepSize()));
+ bindValue(1, backlog.messageid.toQint64() + ((skipSteps + 1) * stepSize()));
skipSteps++;
if (!exec())
return false;
}
}
- backlog.messageid = value(0).toInt();
- backlog.time = QDateTime::fromTime_t(value(1).toInt()).toUTC();
+ backlog.messageid = value(0).toLongLong();
+ // 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.
+ backlog.time = QDateTime::fromMSecsSinceEpoch(value(1).toLongLong()).toUTC();
backlog.bufferid = value(2).toInt();
backlog.type = value(3).toInt();
backlog.flags = value(4).toInt();
- backlog.senderid = value(5).toInt();
- backlog.message = value(6).toString();
+ backlog.senderid = value(5).toLongLong();
+ backlog.senderprefixes = value(6).toString();
+ backlog.message = value(7).toString();
return true;
}
-
-bool SqliteMigrationReader::readMo(IrcServerMO &ircserver)
+bool SqliteMigrationReader::readMo(IrcServerMO& ircserver)
{
if (!next())
return false;
return true;
}
-
-bool SqliteMigrationReader::readMo(UserSettingMO &userSetting)
+bool SqliteMigrationReader::readMo(UserSettingMO& userSetting)
{
if (!next())
return false;
return true;
}
+
+bool SqliteMigrationReader::readMo(CoreStateMO& coreState)
+{
+ if (!next())
+ return false;
+
+ coreState.key = value(0).toString();
+ coreState.value = value(1).toByteArray();
+
+ return true;
+}