#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);
}
void SqliteStorage::createBuffer(UserId user, const NetworkId &networkId, BufferInfo::Type type, const QString &buffer) {
- QSqlQuery &query = cachedQuery("insert_buffer");
+ QSqlQuery query(logDb());
+ query.prepare(queryString("insert_buffer"));
query.bindValue(":userid", user.toInt());
query.bindValue(":networkid", networkId.toInt());
query.bindValue(":buffertype", (int)type);
query.bindValue(":buffername", buffer);
query.bindValue(":buffercname", buffer.toLower());
- query.exec();
+ safeExec(query);
watchQuery(query);
}
BufferInfo SqliteStorage::getBufferInfo(UserId user, const NetworkId &networkId, BufferInfo::Type type, const QString &buffer) {
- QSqlQuery &query = cachedQuery("select_bufferByName");
+ QSqlQuery query(logDb());
+ query.prepare(queryString("select_bufferByName"));
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
}
void SqliteStorage::setBufferLastSeenMsg(UserId user, const BufferId &bufferId, const MsgId &msgId) {
- QSqlQuery &query = cachedQuery("update_buffer_lastseen");
+ QSqlQuery query(logDb());
+ query.prepare(queryString("update_buffer_lastseen"));
+
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;
}
MsgId SqliteStorage::logMessage(Message msg) {
- QSqlQuery &logMessageQuery = cachedQuery("insert_message");
+ QSqlQuery logMessageQuery(logDb());
+ 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());
- 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 = cachedQuery("insert_sender");
+ QSqlQuery addSenderQuery(logDb());
+ addSenderQuery.prepare(queryString("insert_sender"));
addSenderQuery.bindValue(":sender", msg.sender());
- addSenderQuery.exec();
- watchQuery(addSenderQuery);
- 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, int limit, int offset) {
QList<Message> messagelist;
BufferInfo bufferInfo = getBufferInfo(user, bufferId);
offset = 0;
} else {
// we have to determine the real offset first
- QSqlQuery &offsetQuery = cachedQuery("select_messagesOffset");
+ QSqlQuery offsetQuery(logDb());
+ offsetQuery.prepare(queryString("select_messagesOffset"));
+
offsetQuery.bindValue(":bufferid", bufferId.toInt());
offsetQuery.bindValue(":messageid", offset);
- offsetQuery.exec();
+ safeExec(offsetQuery);
offsetQuery.first();
offset = offsetQuery.value(0).toInt();
}
// now let's select the messages
- QSqlQuery &msgQuery = cachedQuery("select_messages");
+ QSqlQuery msgQuery(logDb());
+ msgQuery.prepare(queryString("select_messages"));
+
msgQuery.bindValue(":bufferid", bufferId.toInt());
- msgQuery.bindValue(":limit", lastmsgs);
+ msgQuery.bindValue(":limit", limit);
msgQuery.bindValue(":offset", offset);
- msgQuery.exec();
-
+ safeExec(msgQuery);
+
watchQuery(msgQuery);
-
+
while(msgQuery.next()) {
Message msg(QDateTime::fromTime_t(msgQuery.value(1).toInt()),
bufferInfo,
return messagelist;
// we have to determine the real offset first
- QSqlQuery &offsetQuery = cachedQuery("select_messagesSinceOffset");
+ QSqlQuery offsetQuery(logDb());
+ offsetQuery.prepare(queryString("select_messagesSinceOffset"));
+
offsetQuery.bindValue(":bufferid", bufferId.toInt());
offsetQuery.bindValue(":since", since.toTime_t());
- offsetQuery.exec();
+ safeExec(offsetQuery);
offsetQuery.first();
offset = offsetQuery.value(0).toInt();
// now let's select the messages
- QSqlQuery &msgQuery = cachedQuery("select_messagesSince");
+ 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();
+ safeExec(msgQuery);
watchQuery(msgQuery);
-
+
while(msgQuery.next()) {
Message msg(QDateTime::fromTime_t(msgQuery.value(1).toInt()),
bufferInfo,
if(!bufferInfo.isValid())
return messagelist;
- QSqlQuery &rangeQuery = cachedQuery("select_messageRange");
+ QSqlQuery rangeQuery(logDb());
+ rangeQuery.prepare(queryString("select_messageRange"));
rangeQuery.bindValue(":bufferid", bufferId.toInt());
rangeQuery.bindValue(":firstmsg", first);
rangeQuery.bindValue(":lastmsg", last);
- rangeQuery.exec();
+ safeExec(rangeQuery);
watchQuery(rangeQuery);
-
+
while(rangeQuery.next()) {
Message msg(QDateTime::fromTime_t(rangeQuery.value(1).toInt()),
bufferInfo,
}
QString SqliteStorage::backlogFile() {
- return quasselDir().absolutePath() + "/quassel-storage.sqlite";
+ return quasselDir().absolutePath() + "/quassel-storage.sqlite";
}
+bool SqliteStorage::safeExec(QSqlQuery &query, int retryCount) {
+ query.exec();
-// ONLY NEEDED FOR MIGRATION
-bool SqliteStorage::init(const QVariantMap &settings) {
- if(!AbstractSqlStorage::init(settings))
- return false;
-
- QSqlQuery checkMigratedQuery(logDb());
- checkMigratedQuery.prepare("SELECT DISTINCT typeOf(password) FROM quasseluser");
- checkMigratedQuery.exec();
- if(!watchQuery(checkMigratedQuery))
- return false;
-
- if(!checkMigratedQuery.first())
- return true; // table is empty -> no work to be done
-
- QString passType = checkMigratedQuery.value(0).toString().toLower();
- if(passType == "text")
- return true; // allready migrated
-
- Q_ASSERT(passType == "blob");
-
- QSqlQuery getPasswordsQuery(logDb());
- getPasswordsQuery.prepare("SELECT userid, password FROM quasseluser");
- getPasswordsQuery.exec();
+ if(!query.lastError().isValid())
+ return true;
- if(!watchQuery(getPasswordsQuery)) {
- qCritical() << "unable to migrate to new password format!";
+ 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;
}
-
- QHash<int, QByteArray> passHash;
- while(getPasswordsQuery.next()) {
- passHash[getPasswordsQuery.value(0).toInt()] = getPasswordsQuery.value(1).toByteArray();
- }
-
- QSqlQuery setPasswordsQuery(logDb());
- setPasswordsQuery.prepare("UPDATE quasseluser SET password = :password WHERE userid = :userid");
- foreach(int userId, passHash.keys()) {
- setPasswordsQuery.bindValue(":password", QString(passHash[userId]));
- setPasswordsQuery.bindValue(":userid", userId);
- setPasswordsQuery.exec();
- watchQuery(setPasswordsQuery);
- }
-
- qDebug() << "successfully migrated passwords!";
- return true;
}