#include "util.h"
#include "logger.h"
+int SqliteStorage::_maxRetryCount = 150; // yes this is a large number... only other way to "handle" this is bailing out...
+
SqliteStorage::SqliteStorage(QObject *parent)
: AbstractSqlStorage(parent)
{
query.prepare(queryString("insert_quasseluser"));
query.bindValue(":username", user);
query.bindValue(":password", cryptedPassword(password));
- query.exec();
+ safeExec(query);
if(query.lastError().isValid() && query.lastError().number() == 19) { // user already exists - sadly 19 seems to be the general constraint violation error...
return 0;
}
query.prepare(queryString("select_userid"));
query.bindValue(":username", user);
- query.exec();
+ safeExec(query);
query.first();
UserId uid = query.value(0).toInt();
emit userAdded(uid, user);
query.prepare(queryString("update_userpassword"));
query.bindValue(":userid", user.toInt());
query.bindValue(":password", cryptedPassword(password));
- query.exec();
+ safeExec(query);
}
void SqliteStorage::renameUser(UserId user, const QString &newName) {
query.prepare(queryString("update_username"));
query.bindValue(":userid", user.toInt());
query.bindValue(":username", newName);
- query.exec();
+ safeExec(query);
emit userRenamed(user, newName);
}
query.prepare(queryString("select_authuser"));
query.bindValue(":username", user);
query.bindValue(":password", cryptedPassword(password));
- query.exec();
+ safeExec(query);
if(query.first()) {
return query.value(0).toInt();
UserId SqliteStorage::internalUser() {
QSqlQuery query(logDb());
query.prepare(queryString("select_internaluser"));
- query.exec();
+ safeExec(query);
if(query.first()) {
return query.value(0).toInt();
QSqlQuery query(logDb());
query.prepare(queryString("delete_backlog_by_uid"));
query.bindValue(":userid", user.toInt());
- query.exec();
-
+ safeExec(query);
+
query.prepare(queryString("delete_buffers_by_uid"));
query.bindValue(":userid", user.toInt());
- query.exec();
-
+ safeExec(query);
+
query.prepare(queryString("delete_networks_by_uid"));
query.bindValue(":userid", user.toInt());
- query.exec();
-
+ safeExec(query);
+
query.prepare(queryString("delete_quasseluser"));
query.bindValue(":userid", user.toInt());
- query.exec();
+ safeExec(query);
// I hate the lack of foreign keys and on delete cascade... :(
emit userRemoved(user);
}
query.bindValue(":userid", userId.toInt());
query.bindValue(":settingname", settingName);
query.bindValue(":settingvalue", rawData);
- query.exec();
+ safeExec(query);
if(query.lastError().isValid()) {
QSqlQuery updateQuery(logDb());
updateQuery.bindValue(":userid", userId.toInt());
updateQuery.bindValue(":settingname", settingName);
updateQuery.bindValue(":settingvalue", rawData);
- updateQuery.exec();
+ safeExec(updateQuery);
}
-
+
}
QVariant SqliteStorage::getUserSetting(UserId userId, const QString &settingName, const QVariant &defaultData) {
query.prepare(queryString("select_user_setting"));
query.bindValue(":userid", userId.toInt());
query.bindValue(":settingname", settingName);
- query.exec();
+ safeExec(query);
if(query.first()) {
QVariant data;
query.prepare(queryString("insert_network"));
query.bindValue(":userid", user.toInt());
query.bindValue(":networkname", info.networkName);
- query.exec();
-
+ safeExec(query);
+
networkId = getNetworkId(user, info.networkName);
if(!networkId.isValid()) {
watchQuery(query);
bool SqliteStorage::updateNetwork(UserId user, const NetworkInfo &info) {
if(!isValidNetwork(user, info.networkId))
return false;
-
+
QSqlQuery updateQuery(logDb());
updateQuery.prepare(queryString("update_network"));
updateQuery.bindValue(":networkname", info.networkName);
updateQuery.bindValue(":unlimitedconnectretries", info.unlimitedReconnectRetries ? 1 : 0);
updateQuery.bindValue(":rejoinchannels", info.rejoinChannels ? 1 : 0);
updateQuery.bindValue(":networkid", info.networkId.toInt());
- updateQuery.exec();
+ safeExec(updateQuery);
if(!watchQuery(updateQuery))
return false;
QSqlQuery dropServersQuery(logDb());
dropServersQuery.prepare("DELETE FROM ircserver WHERE networkid = :networkid");
dropServersQuery.bindValue(":networkid", info.networkId.toInt());
- dropServersQuery.exec();
+ safeExec(dropServersQuery);
if(!watchQuery(dropServersQuery))
return false;
insertServersQuery.bindValue(":userid", user.toInt());
insertServersQuery.bindValue(":networkid", info.networkId.toInt());
- insertServersQuery.exec();
+ safeExec(insertServersQuery);
if(!watchQuery(insertServersQuery))
return false;
}
-
+
return true;
}
withTransaction = false;
}
}
-
+
QSqlQuery deleteBacklogQuery(logDb());
deleteBacklogQuery.prepare(queryString("delete_backlog_for_network"));
deleteBacklogQuery.bindValue(":networkid", networkId.toInt());
- deleteBacklogQuery.exec();
+ safeExec(deleteBacklogQuery);
if(!watchQuery(deleteBacklogQuery)) {
if(withTransaction)
logDb().rollback();
return false;
}
-
+
QSqlQuery deleteBuffersQuery(logDb());
deleteBuffersQuery.prepare(queryString("delete_buffers_for_network"));
deleteBuffersQuery.bindValue(":networkid", networkId.toInt());
- deleteBuffersQuery.exec();
+ safeExec(deleteBuffersQuery);
if(!watchQuery(deleteBuffersQuery)) {
if(withTransaction)
logDb().rollback();
return false;
}
-
+
QSqlQuery deleteServersQuery(logDb());
deleteServersQuery.prepare(queryString("delete_ircservers_for_network"));
deleteServersQuery.bindValue(":networkid", networkId.toInt());
- deleteServersQuery.exec();
+ safeExec(deleteServersQuery);
if(!watchQuery(deleteServersQuery)) {
if(withTransaction)
logDb().rollback();
return false;
}
-
+
QSqlQuery deleteNetworkQuery(logDb());
deleteNetworkQuery.prepare(queryString("delete_network"));
deleteNetworkQuery.bindValue(":networkid", networkId.toInt());
- deleteNetworkQuery.exec();
+ safeExec(deleteNetworkQuery);
if(!watchQuery(deleteNetworkQuery)) {
if(withTransaction)
logDb().rollback();
QSqlQuery networksQuery(logDb());
networksQuery.prepare(queryString("select_networks_for_user"));
networksQuery.bindValue(":userid", user.toInt());
-
+
QSqlQuery serversQuery(logDb());
serversQuery.prepare(queryString("select_servers_for_network"));
- networksQuery.exec();
+ safeExec(networksQuery);
if(!watchQuery(networksQuery))
return nets;
net.rejoinChannels = networksQuery.value(15).toInt() == 1 ? true : false;
serversQuery.bindValue(":networkid", net.networkId.toInt());
- serversQuery.exec();
+ safeExec(serversQuery);
if(!watchQuery(serversQuery))
return nets;
query.prepare(queryString("select_networkExists"));
query.bindValue(":userid", user.toInt());
query.bindValue(":networkid", networkId.toInt());
- query.exec();
+ safeExec(query);
watchQuery(query); // there should not occur any errors
if(!query.first())
return false;
-
+
Q_ASSERT(!query.next());
return true;
}
query.prepare(queryString("select_bufferExists"));
query.bindValue(":userid", user.toInt());
query.bindValue(":bufferid", bufferId.toInt());
- query.exec();
+ safeExec(query);
watchQuery(query);
if(!query.first())
"WHERE userid = :userid AND networkname = :networkname");
query.bindValue(":userid", user.toInt());
query.bindValue(":networkname", network);
- query.exec();
-
+ safeExec(query);
+
if(query.first())
return query.value(0).toInt();
else
QSqlQuery query(logDb());
query.prepare(queryString("select_connected_networks"));
query.bindValue(":userid", user.toInt());
- query.exec();
+ safeExec(query);
watchQuery(query);
while(query.next()) {
connectedNets << query.value(0).toInt();
}
-
+
return connectedNets;
}
query.bindValue(":userid", user.toInt());
query.bindValue(":networkid", networkId.toInt());
query.bindValue(":connected", isConnected ? 1 : 0);
- query.exec();
+ safeExec(query);
watchQuery(query);
}
query.prepare(queryString("select_persistent_channels"));
query.bindValue(":userid", user.toInt());
query.bindValue(":networkid", networkId.toInt());
- query.exec();
+ safeExec(query);
watchQuery(query);
while(query.next()) {
persistentChans[query.value(0).toString()] = query.value(1).toString();
}
-
+
return persistentChans;
}
query.bindValue(":networkId", networkId.toInt());
query.bindValue(":buffercname", channel.toLower());
query.bindValue(":joined", isJoined ? 1 : 0);
- query.exec();
+ safeExec(query);
watchQuery(query);
}
query.bindValue(":networkId", networkId.toInt());
query.bindValue(":buffercname", channel.toLower());
query.bindValue(":key", key);
- query.exec();
+ safeExec(query);
watchQuery(query);
}
query.bindValue(":buffertype", (int)type);
query.bindValue(":buffername", buffer);
query.bindValue(":buffercname", buffer.toLower());
- query.exec();
+ safeExec(query);
watchQuery(query);
}
query.bindValue(":networkid", networkId.toInt());
query.bindValue(":userid", user.toInt());
query.bindValue(":buffercname", buffer.toLower());
- query.exec();
+ safeExec(query);
if(!query.first()) {
createBuffer(user, networkId, type, buffer);
- query.exec();
+ safeExec(query);
if(!query.first()) {
watchQuery(query);
qWarning() << "unable to create BufferInfo for:" << user << networkId << buffer;
query.prepare(queryString("select_buffer_by_id"));
query.bindValue(":userid", user.toInt());
query.bindValue(":bufferid", bufferId.toInt());
- query.exec();
+ safeExec(query);
if(!watchQuery(query))
return BufferInfo();
QSqlQuery query(logDb());
query.prepare(queryString("select_buffers"));
query.bindValue(":userid", user.toInt());
-
- query.exec();
+
+ 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());
query.bindValue(":networkid", networkId.toInt());
query.bindValue(":userid", user.toInt());
- query.exec();
+ safeExec(query);
watchQuery(query);
while(query.next()) {
bufferList << BufferId(query.value(0).toInt());
QSqlQuery delBacklogQuery(logDb());
delBacklogQuery.prepare(queryString("delete_backlog_for_buffer"));
delBacklogQuery.bindValue(":bufferid", bufferId.toInt());
- delBacklogQuery.exec();
+ safeExec(delBacklogQuery);
if(!watchQuery(delBacklogQuery))
return false;
QSqlQuery delBufferQuery(logDb());
delBufferQuery.prepare(queryString("delete_buffer_for_bufferid"));
delBufferQuery.bindValue(":bufferid", bufferId.toInt());
- delBufferQuery.exec();
+ safeExec(delBufferQuery);
if(!watchQuery(delBufferQuery))
return false;
existsQuery.bindValue(":networkid", networkId.toInt());
existsQuery.bindValue(":userid", user.toInt());
existsQuery.bindValue(":buffercname", oldName.toLower());
- existsQuery.exec();
+ safeExec(existsQuery);
if(!watchQuery(existsQuery))
return false;
existsQuery.bindValue(":networkid", networkId.toInt());
existsQuery.bindValue(":userid", user.toInt());
existsQuery.bindValue(":buffercname", newName.toLower());
- existsQuery.exec();
+ safeExec(existsQuery);
if(!watchQuery(existsQuery))
return false;
renameBufferQuery.bindValue(":buffername", newName);
renameBufferQuery.bindValue(":buffercname", newName.toLower());
renameBufferQuery.bindValue(":bufferid", bufferid);
- renameBufferQuery.exec();
+ safeExec(renameBufferQuery);
if(watchQuery(existsQuery))
return BufferId(bufferid);
else
query.bindValue(":userid", user.toInt());
query.bindValue(":bufferid", bufferId.toInt());
query.bindValue(":lastseenmsgid", msgId.toInt());
- query.exec();
+ safeExec(query);
watchQuery(query);
}
QSqlQuery query(logDb());
query.prepare(queryString("select_buffer_lastseen_messages"));
query.bindValue(":userid", user.toInt());
- query.exec();
+ safeExec(query);
if(!watchQuery(query))
return lastSeenHash;
logMessageQuery.bindValue(":flags", (int)msg.flags());
logMessageQuery.bindValue(":sender", msg.sender());
logMessageQuery.bindValue(":message", msg.contents());
- logMessageQuery.exec();
-
+ safeExec(logMessageQuery);
+
if(logMessageQuery.lastError().isValid()) {
// constraint violation - must be NOT NULL constraint - probably the sender is missing...
if(logMessageQuery.lastError().number() == 19) {
QSqlQuery addSenderQuery(logDb());
addSenderQuery.prepare(queryString("insert_sender"));
addSenderQuery.bindValue(":sender", msg.sender());
- addSenderQuery.exec();
- logMessageQuery.exec();
+ safeExec(addSenderQuery);
+ safeExec(logMessageQuery);
if(!watchQuery(logMessageQuery))
return 0;
} else {
return msgId;
}
-QList<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, int lastmsgs, int offset) {
+QList<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, MsgId first, MsgId last, int limit) {
QList<Message> messagelist;
BufferInfo bufferInfo = getBufferInfo(user, bufferId);
if(!bufferInfo.isValid())
return messagelist;
- if(offset == -1) {
- offset = 0;
+ QSqlQuery query(logDb());
+ if(last == -1) {
+ query.prepare(queryString("select_messagesNew"));
} else {
- // we have to determine the real offset first
- QSqlQuery offsetQuery(logDb());
- offsetQuery.prepare(queryString("select_messagesOffset"));
-
- offsetQuery.bindValue(":bufferid", bufferId.toInt());
- offsetQuery.bindValue(":messageid", offset);
- offsetQuery.exec();
- offsetQuery.first();
- offset = offsetQuery.value(0).toInt();
+ query.prepare(queryString("select_messages"));
+ query.bindValue(":lastmsg", last.toInt());
}
- // now let's select the messages
- QSqlQuery msgQuery(logDb());
- msgQuery.prepare(queryString("select_messages"));
-
- msgQuery.bindValue(":bufferid", bufferId.toInt());
- msgQuery.bindValue(":limit", lastmsgs);
- msgQuery.bindValue(":offset", offset);
- msgQuery.exec();
-
- watchQuery(msgQuery);
-
- while(msgQuery.next()) {
- Message msg(QDateTime::fromTime_t(msgQuery.value(1).toInt()),
+ query.bindValue(":bufferid", bufferId.toInt());
+ query.bindValue(":firstmsg", first.toInt());
+ query.bindValue(":limit", limit);
+ safeExec(query);
+
+ watchQuery(query);
+
+ while(query.next()) {
+ Message msg(QDateTime::fromTime_t(query.value(1).toInt()),
bufferInfo,
- (Message::Type)msgQuery.value(2).toUInt(),
- msgQuery.value(5).toString(),
- msgQuery.value(4).toString(),
- (Message::Flags)msgQuery.value(3).toUInt());
- msg.setMsgId(msgQuery.value(0).toInt());
+ (Message::Type)query.value(2).toUInt(),
+ query.value(5).toString(),
+ query.value(4).toString(),
+ (Message::Flags)query.value(3).toUInt());
+ msg.setMsgId(query.value(0).toInt());
messagelist << msg;
}
return messagelist;
}
-
-QList<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, QDateTime since, int offset) {
+QList<Message> SqliteStorage::requestAllMsgs(UserId user, MsgId first, MsgId last, int limit) {
QList<Message> messagelist;
- BufferInfo bufferInfo = getBufferInfo(user, bufferId);
- if(!bufferInfo.isValid())
- return messagelist;
-
- // we have to determine the real offset first
- QSqlQuery offsetQuery(logDb());
- offsetQuery.prepare(queryString("select_messagesSinceOffset"));
-
- offsetQuery.bindValue(":bufferid", bufferId.toInt());
- offsetQuery.bindValue(":since", since.toTime_t());
- offsetQuery.exec();
- offsetQuery.first();
- offset = offsetQuery.value(0).toInt();
-
- // now let's select the messages
- QSqlQuery msgQuery(logDb());
- msgQuery.prepare(queryString("select_messagesSince"));
- msgQuery.bindValue(":bufferid", bufferId.toInt());
- msgQuery.bindValue(":since", since.toTime_t());
- msgQuery.bindValue(":offset", offset);
- msgQuery.exec();
-
- watchQuery(msgQuery);
-
- while(msgQuery.next()) {
- Message msg(QDateTime::fromTime_t(msgQuery.value(1).toInt()),
- bufferInfo,
- (Message::Type)msgQuery.value(2).toUInt(),
- msgQuery.value(5).toString(),
- msgQuery.value(4).toString(),
- (Message::Flags)msgQuery.value(3).toUInt());
- msg.setMsgId(msgQuery.value(0).toInt());
- messagelist << msg;
+ QHash<BufferId, BufferInfo> bufferInfoHash;
+ foreach(BufferInfo bufferInfo, requestBuffers(user)) {
+ bufferInfoHash[bufferInfo.bufferId()] = bufferInfo;
}
- return messagelist;
-}
-
-
-QList<Message> SqliteStorage::requestMsgRange(UserId user, BufferId bufferId, int first, int last) {
- QList<Message> messagelist;
+ QSqlQuery query(logDb());
+ if(last == -1) {
+ query.prepare(queryString("select_messagesAllNew"));
+ } else {
+ query.prepare(queryString("select_messagesAll"));
+ query.bindValue(":lastmsg", last.toInt());
+ }
+ query.bindValue(":userid", user.toInt());
+ query.bindValue(":firstmsg", first.toInt());
+ query.bindValue(":limit", limit);
+ safeExec(query);
- BufferInfo bufferInfo = getBufferInfo(user, bufferId);
- if(!bufferInfo.isValid())
- return messagelist;
+ watchQuery(query);
- QSqlQuery rangeQuery(logDb());
- rangeQuery.prepare(queryString("select_messageRange"));
- rangeQuery.bindValue(":bufferid", bufferId.toInt());
- rangeQuery.bindValue(":firstmsg", first);
- rangeQuery.bindValue(":lastmsg", last);
- rangeQuery.exec();
-
- watchQuery(rangeQuery);
-
- while(rangeQuery.next()) {
- Message msg(QDateTime::fromTime_t(rangeQuery.value(1).toInt()),
- bufferInfo,
- (Message::Type)rangeQuery.value(2).toUInt(),
- rangeQuery.value(5).toString(),
- rangeQuery.value(4).toString(),
- (Message::Flags)rangeQuery.value(3).toUInt());
- msg.setMsgId(rangeQuery.value(0).toInt());
+ while(query.next()) {
+ Message msg(QDateTime::fromTime_t(query.value(2).toInt()),
+ bufferInfoHash[query.value(1).toInt()],
+ (Message::Type)query.value(3).toUInt(),
+ query.value(6).toString(),
+ query.value(5).toString(),
+ (Message::Flags)query.value(4).toUInt());
+ msg.setMsgId(query.value(0).toInt());
messagelist << msg;
}
}
QString SqliteStorage::backlogFile() {
- return quasselDir().absolutePath() + "/quassel-storage.sqlite";
+ return quasselDir().absolutePath() + "/quassel-storage.sqlite";
+}
+
+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 */
+ if(retryCount < _maxRetryCount)
+ return safeExec(query, retryCount + 1);
+ default:
+ return false;
+ }
}