#include "network.h"
#include "quassel.h"
-int PostgreSqlStorage::_maxRetryCount = 150; // yes this is a large number... only other way to "handle" this is bailing out...
-
PostgreSqlStorage::PostgreSqlStorage(QObject *parent)
: AbstractSqlStorage(parent),
_port(-1)
PostgreSqlStorage::~PostgreSqlStorage() {
}
+AbstractSqlMigrationWriter *PostgreSqlStorage::createMigrationWriter() {
+ PostgreSqlMigrationWriter *writer = new PostgreSqlMigrationWriter();
+ QVariantMap properties;
+ properties["Username"] = _userName;
+ properties["Password"] = _password;
+ properties["Hostname"] = _hostName;
+ properties["Port"] = _port;
+ properties["Database"] = _databaseName;
+ writer->setConnectionProperties(properties);
+ return writer;
+}
+
bool PostgreSqlStorage::isAvailable() const {
if(!QSqlDatabase::isDriverAvailable("QPSQL")) return false;
return true;
return tr("PostgreSQL Turbo Bomber HD!");
}
-QVariantMap PostgreSqlStorage::setupKeys() const {
+QStringList PostgreSqlStorage::setupKeys() const {
+ QStringList keys;
+ keys << "Username"
+ << "Password"
+ << "Hostname"
+ << "Port"
+ << "Database";
+ return keys;
+}
+QVariantMap PostgreSqlStorage::setupDefaults() const {
QVariantMap map;
map["Username"] = QVariant(QString("quassel"));
- map["Password"] = QVariant(QString());
map["Hostname"] = QVariant(QString("localhost"));
map["Port"] = QVariant(5432);
map["Database"] = QVariant(QString("quassel"));
return map;
}
+void PostgreSqlStorage::initDbSession(QSqlDatabase &db) {
+ // this blows... but unfortunately Qt's PG driver forces us to this...
+ db.exec("set standard_conforming_strings = off");
+ db.exec("set escape_string_warning = off");
+}
+
void PostgreSqlStorage::setConnectionProperties(const QVariantMap &properties) {
_userName = properties["Username"].toString();
_password = properties["Password"].toString();
return uid;
}
-void PostgreSqlStorage::updateUser(UserId user, const QString &password) {
+bool PostgreSqlStorage::updateUser(UserId user, const QString &password) {
QSqlQuery query(logDb());
query.prepare(queryString("update_userpassword"));
query.bindValue(":userid", user.toInt());
query.bindValue(":password", cryptedPassword(password));
safeExec(query);
+ return query.numRowsAffected() != 0;
}
void PostgreSqlStorage::renameUser(UserId user, const QString &newName) {
}
}
+UserId PostgreSqlStorage::getUserId(const QString &user) {
+ QSqlQuery query(logDb());
+ query.prepare(queryString("select_userid"));
+ query.bindValue(":username", user);
+ safeExec(query);
+
+ if(query.first()) {
+ return query.value(0).toInt();
+ } else {
+ return 0;
+ }
+}
+
UserId PostgreSqlStorage::internalUser() {
QSqlQuery query(logDb());
query.prepare(queryString("select_internaluser"));
out.setVersion(QDataStream::Qt_4_2);
out << data;
- QSqlDatabase db = logDb();
+ QSqlDatabase db = logDb();
QSqlQuery query(db);
query.prepare(queryString("insert_user_setting"));
query.bindValue(":userid", userId.toInt());
return IdentityId();
}
- qDebug() << "creatId" << query.first() << query.value(0).toInt();
+ query.first();
identityId = query.value(0).toInt();
identity.setId(identityId);
qWarning() << " -" << qPrintable(db.lastError().text());
return false;
}
-
+
QSqlQuery checkQuery(db);
checkQuery.prepare(queryString("select_checkidentity"));
checkQuery.bindValue(":identityid", identity.id().toInt());
db.rollback();
return false;
}
-
+
QSqlQuery deleteNickQuery(db);
deleteNickQuery.prepare(queryString("delete_nicks"));
deleteNickQuery.bindValue(":identityid", identity.id().toInt());
return NetworkId();
}
- qDebug() << "createNet:" << query.first() << query.value(0).toInt();
+ query.first();
networkId = query.value(0).toInt();
if(!networkId.isValid()) {
}
if(!db.commit()) {
- qWarning() << "PostgreSqlStorage::updateNetwork(): commiting data failed!";
+ qWarning() << "PostgreSqlStorage::createNetwork(): commiting data failed!";
qWarning() << " -" << qPrintable(db.lastError().text());
return NetworkId();
}
void PostgreSqlStorage::bindNetworkInfo(QSqlQuery &query, const NetworkInfo &info) {
query.bindValue(":networkname", info.networkName);
- query.bindValue(":identityid", info.identity.toInt());
+ query.bindValue(":identityid", info.identity.isValid() ? info.identity.toInt() : QVariant());
query.bindValue(":encodingcodec", QString(info.codecForEncoding));
query.bindValue(":decodingcodec", QString(info.codecForDecoding));
query.bindValue(":servercodec", QString(info.codecForServer));
}
bool PostgreSqlStorage::updateNetwork(UserId user, const NetworkInfo &info) {
- if(!isValidNetwork(user, info.networkId))
- return false;
-
QSqlDatabase db = logDb();
if(!db.transaction()) {
qWarning() << "PostgreSqlStorage::updateNetwork(): failed to begin transaction!";
QSqlQuery updateQuery(db);
updateQuery.prepare(queryString("update_network"));
+ updateQuery.bindValue(":userid", user.toInt());
bindNetworkInfo(updateQuery, info);
safeExec(updateQuery);
if(!watchQuery(updateQuery)) {
db.rollback();
return false;
}
+ if(updateQuery.numRowsAffected() != 1) {
+ // seems this is not our network...
+ db.rollback();
+ return false;
+ }
QSqlQuery dropServersQuery(db);
dropServersQuery.prepare("DELETE FROM ircserver WHERE networkid = :networkid");
}
bool PostgreSqlStorage::removeNetwork(UserId user, const NetworkId &networkId) {
- if(!isValidNetwork(user, networkId))
- return false;
-
QSqlDatabase db = logDb();
if(!db.transaction()) {
qWarning() << "PostgreSqlStorage::removeNetwork(): cannot start transaction!";
QSqlQuery query(db);
query.prepare(queryString("delete_network"));
+ query.bindValue(":userid", user.toInt());
query.bindValue(":networkid", networkId.toInt());
safeExec(query);
if(!watchQuery(query)) {
qWarning() << " -" << qPrintable(db.lastError().text());
return nets;
}
-
+
QSqlQuery networksQuery(db);
networksQuery.prepare(queryString("select_networks_for_user"));
networksQuery.bindValue(":userid", user.toInt());
return nets;
}
-bool PostgreSqlStorage::isValidNetwork(UserId user, const NetworkId &networkId) {
- QSqlQuery query(logDb());
- query.prepare(queryString("select_networkExists"));
- query.bindValue(":userid", user.toInt());
- query.bindValue(":networkid", networkId.toInt());
- safeExec(query);
-
- watchQuery(query);
- if(!query.first())
- return false;
-
- Q_ASSERT(!query.next());
- return true;
-}
-
-bool PostgreSqlStorage::isValidBuffer(const UserId &user, const BufferId &bufferId) {
- QSqlQuery query(logDb());
- query.prepare(queryString("select_bufferExists"));
- query.bindValue(":userid", user.toInt());
- query.bindValue(":bufferid", bufferId.toInt());
- safeExec(query);
-
- watchQuery(query);
- if(!query.first())
- return false;
-
- Q_ASSERT(!query.next());
- return true;
-}
-
QList<NetworkId> PostgreSqlStorage::connectedNetworks(UserId user) {
QList<NetworkId> connectedNets;
if(query.first()) {
BufferInfo bufferInfo = BufferInfo(query.value(0).toInt(), networkId, (BufferInfo::Type)query.value(1).toInt(), 0, buffer);
if(query.next()) {
- qCritical() << "PostgreSqlStorage::getBufferInfo(): received more then one Buffer!";
+ qCritical() << "PostgreSqlStorage::bufferInfo(): received more then one Buffer!";
qCritical() << " Query:" << query.lastQuery();
qCritical() << " bound Values:";
QList<QVariant> list = query.boundValues().values();
qWarning() << " -" << qPrintable(db.lastError().text());
return bufferlist;
}
-
+
QSqlQuery query(db);
query.prepare(queryString("select_buffers"));
query.bindValue(":userid", user.toInt());
qWarning() << " -" << qPrintable(db.lastError().text());
return bufferList;
}
-
+
QSqlQuery query(db);
query.prepare(queryString("select_buffers_for_network"));
query.bindValue(":networkid", networkId.toInt());
}
bool PostgreSqlStorage::removeBuffer(const UserId &user, const BufferId &bufferId) {
- if(!isValidBuffer(user, bufferId))
- return false;
-
QSqlDatabase db = logDb();
if(!db.transaction()) {
qWarning() << "PostgreSqlStorage::removeBuffer(): cannot start transaction!";
QSqlQuery query(db);
query.prepare(queryString("delete_buffer_for_bufferid"));
+ query.bindValue(":userid", user.toInt());
query.bindValue(":bufferid", bufferId.toInt());
safeExec(query);
if(!watchQuery(query)) {
db.rollback();
return false;
}
- db.commit();
- return true;
+
+ int numRows = query.numRowsAffected();
+ switch(numRows) {
+ case 0:
+ db.commit();
+ return false;
+ case 1:
+ db.commit();
+ return true;
+ default:
+ // there was more then one buffer deleted...
+ qWarning() << "PostgreSqlStorage::removeBuffer(): Userid" << user << "BufferId" << "caused deletion of" << numRows << "Buffers! Rolling back transaction...";
+ db.rollback();
+ return false;
+ }
}
bool PostgreSqlStorage::renameBuffer(const UserId &user, const BufferId &bufferId, const QString &newName) {
- if(!isValidBuffer(user, bufferId))
+ QSqlDatabase db = logDb();
+ if(!db.transaction()) {
+ qWarning() << "PostgreSqlStorage::renameBuffer(): cannot start transaction!";
return false;
+ }
- QSqlQuery query(logDb());
+ QSqlQuery query(db);
query.prepare(queryString("update_buffer_name"));
query.bindValue(":buffername", newName);
query.bindValue(":buffercname", newName.toLower());
+ query.bindValue(":userid", user.toInt());
query.bindValue(":bufferid", bufferId.toInt());
safeExec(query);
if(query.lastError().isValid()) {
+ watchQuery(query);
+ db.rollback();
return false;
}
- return true;
-}
-bool PostgreSqlStorage::mergeBuffersPermanently(const UserId &user, const BufferId &bufferId1, const BufferId &bufferId2) {
- if(!isValidBuffer(user, bufferId1) || !isValidBuffer(user, bufferId2))
+ int numRows = query.numRowsAffected();
+ switch(numRows) {
+ case 0:
+ db.commit();
return false;
+ case 1:
+ db.commit();
+ return true;
+ default:
+ // there was more then one buffer deleted...
+ qWarning() << "PostgreSqlStorage::renameBuffer(): Userid" << user << "BufferId" << "affected" << numRows << "Buffers! Rolling back transaction...";
+ db.rollback();
+ return false;
+ }
+}
+bool PostgreSqlStorage::mergeBuffersPermanently(const UserId &user, const BufferId &bufferId1, const BufferId &bufferId2) {
QSqlDatabase db = logDb();
if(!db.transaction()) {
qWarning() << "PostgreSqlStorage::mergeBuffersPermanently(): cannot start transaction!";
return false;
}
+ QSqlQuery checkQuery(db);
+ checkQuery.prepare("SELECT count(*) FROM buffer "
+ "WHERE userid = :userid AND bufferid IN (:buffer1, :buffer2)");
+ checkQuery.bindValue(":userid", user.toInt());
+ checkQuery.bindValue(":buffer1", bufferId1.toInt());
+ checkQuery.bindValue(":buffer2", bufferId2.toInt());
+ safeExec(checkQuery);
+ if(!watchQuery(checkQuery)) {
+ db.rollback();
+ return false;
+ }
+ checkQuery.first();
+ if(checkQuery.value(0).toInt() != 2) {
+ db.rollback();
+ return false;
+ }
+
QSqlQuery query(db);
query.prepare(queryString("update_backlog_bufferid"));
query.bindValue(":oldbufferid", bufferId2.toInt());
return lastSeenHash;
}
-MsgId PostgreSqlStorage::logMessage(Message msg) {
+bool PostgreSqlStorage::logMessage(Message &msg) {
QSqlDatabase db = logDb();
if(!db.transaction()) {
qWarning() << "PostgreSqlStorage::logMessage(): cannot start transaction!";
return 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());
- safeExec(logMessageQuery);
+ if(!prepareQuery("insert_message", queryString("insert_message"), db)) {
+ qWarning() << "PostgreSqlStorage::logMessages(): unable to prepare query:" << queryString("insert_message");
+ qWarning() << " Error:" << db.lastError().text();
+ db.rollback();
+ return false;
+ }
+
+ QVariantList params;
+ params << msg.timestamp()
+ << msg.bufferInfo().bufferId().toInt()
+ << msg.type()
+ << (int)msg.flags()
+ << msg.sender()
+ << msg.contents();
+ QSqlQuery logMessageQuery = executePreparedQuery("insert_message", params, db);
if(logMessageQuery.lastError().isValid()) {
// first we need to reset the transaction
db.rollback();
- db.transaction();
+ db.transaction();
- QSqlQuery addSenderQuery(db);
- addSenderQuery.prepare(queryString("insert_sender"));
- addSenderQuery.bindValue(":sender", msg.sender());
- safeExec(addSenderQuery);
- safeExec(logMessageQuery);
+ // it's possible that the sender was already added by another thread
+ // since the insert might fail we're setting a savepoint
+ savePoint("sender_sp1", db);
+ QSqlQuery addSenderQuery = executePreparedQuery("insert_sender", msg.sender(), db);
+ if(addSenderQuery.lastError().isValid())
+ rollbackSavePoint("sender_sp1", db);
+ else
+ releaseSavePoint("sender_sp1", db);
+
+ logMessageQuery = db.exec(logMessageQuery.lastQuery());
if(!watchQuery(logMessageQuery)) {
+ qDebug() << "==================== Sender Query:";
+ watchQuery(addSenderQuery);
+ qDebug() << "==================== /Sender Query";
db.rollback();
- return MsgId();
+ return false;
}
}
logMessageQuery.first();
MsgId msgId = logMessageQuery.value(0).toInt();
db.commit();
+ if(msgId.isValid()) {
+ msg.setMsgId(msgId);
+ return true;
+ } else {
+ return false;
+ }
+}
- Q_ASSERT(msgId.isValid());
- return msgId;
+bool PostgreSqlStorage::logMessages(MessageList &msgs) {
+ QSqlDatabase db = logDb();
+ if(!db.transaction()) {
+ qWarning() << "PostgreSqlStorage::logMessage(): cannot start transaction!";
+ qWarning() << " -" << qPrintable(db.lastError().text());
+ return false;
+ }
+
+ if(!prepareQuery("insert_sender", queryString("insert_sender"), db)) {
+ qWarning() << "PostgreSqlStorage::logMessages(): unable to prepare query:" << queryString("insert_sender");
+ qWarning() << " Error:" << db.lastError().text();
+ db.rollback();
+ return false;
+ }
+ QSet<QString> senders;
+ for(int i = 0; i < msgs.count(); i++) {
+ const QString &sender = msgs.at(i).sender();
+ if(senders.contains(sender))
+ continue;
+ senders << sender;
+
+ savePoint("sender_sp", db);
+ QSqlQuery addSenderQuery = executePreparedQuery("insert_sender", sender, db);
+ if(addSenderQuery.lastError().isValid())
+ rollbackSavePoint("sender_sp", db);
+ else
+ releaseSavePoint("sender_sp", db);
+ }
+
+ // yes we loop twice over the same list. This avoids alternating queries.
+ if(!prepareQuery("insert_message", queryString("insert_message"), db)) {
+ qWarning() << "PostgreSqlStorage::logMessages(): unable to prepare query:" << queryString("insert_message");
+ qWarning() << " Error:" << db.lastError().text();
+ db.rollback();
+ return false;
+ }
+ bool error = false;
+ for(int i = 0; i < msgs.count(); i++) {
+ Message &msg = msgs[i];
+ QVariantList params;
+ params << msg.timestamp()
+ << msg.bufferInfo().bufferId().toInt()
+ << msg.type()
+ << (int)msg.flags()
+ << msg.sender()
+ << msg.contents();
+ QSqlQuery logMessageQuery = executePreparedQuery("insert_message", params, db);
+ if(!watchQuery(logMessageQuery)) {
+ db.rollback();
+ error = true;
+ break;
+ } else {
+ logMessageQuery.first();
+ msg.setMsgId(logMessageQuery.value(0).toInt());
+ }
+ }
+
+ if(error) {
+ // we had a rollback in the db so we need to reset all msgIds
+ for(int i = 0; i < msgs.count(); i++) {
+ msgs[i].setMsgId(MsgId());
+ }
+ return false;
+ }
+
+ db.commit();
+ return true;
}
QList<Message> PostgreSqlStorage::requestMsgs(UserId user, BufferId bufferId, MsgId first, MsgId last, int limit) {
QList<Message> messagelist;
+ QSqlDatabase db = logDb();
+ if(!beginReadOnlyTransaction(db)) {
+ qWarning() << "PostgreSqlStorage::requestMsgs(): cannot start read only transaction!";
+ qWarning() << " -" << qPrintable(db.lastError().text());
+ return messagelist;
+ }
+
BufferInfo bufferInfo = getBufferInfo(user, bufferId);
- if(!bufferInfo.isValid())
+ if(!bufferInfo.isValid()) {
+ db.rollback();
return messagelist;
+ }
- QSqlQuery query(logDb());
-
+ QString queryName;
+ QVariantList params;
if(last == -1 && first == -1) {
- query.prepare(queryString("select_messagesNewestK"));
+ queryName = "select_messages";
} else if(last == -1) {
- query.prepare(queryString("select_messagesNewerThan"));
- query.bindValue(":firstmsg", first.toInt());
+ queryName = "select_messagesNewerThan";
+ params << first.toInt();
} else {
- query.prepare(queryString("select_messages"));
- query.bindValue(":lastmsg", last.toInt());
- query.bindValue(":firstmsg", first.toInt());
+ queryName = "select_messagesRange";
+ params << first.toInt();
+ params << last.toInt();
+ }
+ params << bufferId.toInt();
+ if(limit != -1)
+ params << limit;
+ else
+ params << "ALL";
+
+ if(!prepareQuery(queryName, queryString(queryName), db)) {
+ qWarning() << "PostgreSqlStorage::logMessages(): unable to prepare query:" << queryString(queryName);
+ qWarning() << " Error:" << db.lastError().text();
+ db.rollback();
+ return messagelist;
}
- query.bindValue(":bufferid", bufferId.toInt());
- query.bindValue(":limit", limit);
- safeExec(query);
+ QSqlQuery query = executePreparedQuery(queryName, params, db);
- watchQuery(query);
+ if(!watchQuery(query)) {
+ qDebug() << "select_messages failed";
+ db.rollback();
+ return messagelist;
+ }
+ QDateTime timestamp;
while(query.next()) {
- Message msg(QDateTime::fromTime_t(query.value(1).toInt()),
- bufferInfo,
- (Message::Type)query.value(2).toUInt(),
- query.value(5).toString(),
- query.value(4).toString(),
- (Message::Flags)query.value(3).toUInt());
+ timestamp = query.value(1).toDateTime();
+ timestamp.setTimeSpec(Qt::UTC);
+ Message msg(timestamp,
+ bufferInfo,
+ (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;
}
+
+ db.commit();
return messagelist;
}
QList<Message> PostgreSqlStorage::requestAllMsgs(UserId user, MsgId first, MsgId last, int limit) {
QList<Message> messagelist;
+ // requestBuffers uses it's own transaction.
QHash<BufferId, BufferInfo> bufferInfoHash;
foreach(BufferInfo bufferInfo, requestBuffers(user)) {
bufferInfoHash[bufferInfo.bufferId()] = bufferInfo;
}
- QSqlQuery query(logDb());
+ QSqlDatabase db = logDb();
+ if(!beginReadOnlyTransaction(db)) {
+ qWarning() << "PostgreSqlStorage::requestAllMsgs(): cannot start read only transaction!";
+ qWarning() << " -" << qPrintable(db.lastError().text());
+ return messagelist;
+ }
+
+ QSqlQuery query(db);
if(last == -1) {
query.prepare(queryString("select_messagesAllNew"));
} else {
}
query.bindValue(":userid", user.toInt());
query.bindValue(":firstmsg", first.toInt());
- query.bindValue(":limit", limit);
safeExec(query);
+ if(!watchQuery(query)) {
+ db.rollback();
+ return messagelist;
+ }
- watchQuery(query);
-
- while(query.next()) {
- Message msg(QDateTime::fromTime_t(query.value(2).toInt()),
+ QDateTime timestamp;
+ for(int i = 0; i < limit && query.next(); i++) {
+ timestamp = query.value(1).toDateTime();
+ timestamp.setTimeSpec(Qt::UTC);
+ Message msg(timestamp,
bufferInfoHash[query.value(1).toInt()],
(Message::Type)query.value(3).toUInt(),
query.value(6).toString(),
messagelist << msg;
}
+ db.commit();
return messagelist;
}
QSqlQuery query = db.exec("BEGIN TRANSACTION READ ONLY");
return !query.lastError().isValid();
}
+
+bool PostgreSqlStorage::prepareQuery(const QString &handle, const QString &query, const QSqlDatabase &db) {
+ if(_preparedQueries.contains(db.connectionName()) && _preparedQueries[db.connectionName()].contains(handle))
+ return true; // already prepared
+
+ QMutexLocker locker(&_queryHashMutex);
+
+ static unsigned int stmtCount = 0;
+ QString queryId = QLatin1String("quassel_") + QString::number(++stmtCount, 16);
+ // qDebug() << "prepare:" << QString("PREPARE %1 AS %2").arg(queryId).arg(query);
+ db.exec(QString("PREPARE %1 AS %2").arg(queryId).arg(query));
+ if(db.lastError().isValid()) {
+ return false;
+ } else {
+ _preparedQueries[db.connectionName()][handle] = queryId;
+ return true;
+ }
+}
+
+QSqlQuery PostgreSqlStorage::executePreparedQuery(const QString &handle, const QVariantList ¶ms, const QSqlDatabase &db) {
+ if(!_preparedQueries.contains(db.connectionName()) || !_preparedQueries[db.connectionName()].contains(handle)) {
+ qWarning() << "PostgreSqlStorage::executePreparedQuery() no prepared Query with handle" << handle << "on Database" << db.connectionName();
+ return QSqlQuery(db);
+ }
+
+ QSqlDriver *driver = db.driver();
+
+ QStringList paramStrings;
+ QSqlField field;
+ for(int i = 0; i < params.count(); i++) {
+ const QVariant &value = params.at(i);
+ field.setType(value.type());
+ if(value.isNull())
+ field.clear();
+ else
+ field.setValue(value);
+
+ paramStrings << driver->formatValue(field);
+ }
+
+ const QString &queryId = _preparedQueries[db.connectionName()][handle];
+ if(params.isEmpty()) {
+ return db.exec(QString("EXECUTE %1").arg(queryId));
+ } else {
+ // qDebug() << "preparedExec:" << QString("EXECUTE %1 (%2)").arg(queryId).arg(paramStrings.join(", "));
+ return db.exec(QString("EXECUTE %1 (%2)").arg(queryId).arg(paramStrings.join(", ")));
+ }
+}
+
+QSqlQuery PostgreSqlStorage::executePreparedQuery(const QString &handle, const QVariant ¶m, const QSqlDatabase &db) {
+ if(!_preparedQueries.contains(db.connectionName()) || !_preparedQueries[db.connectionName()].contains(handle)) {
+ qWarning() << "PostgreSqlStorage::executePreparedQuery() no prepared Query with handle" << handle << "on Database" << db.connectionName();
+ return QSqlQuery(db);
+ }
+
+ QSqlField field;
+ field.setType(param.type());
+ if(param.isNull())
+ field.clear();
+ else
+ field.setValue(param);
+
+ const QString &queryId = _preparedQueries[db.connectionName()][handle];
+ QString paramString = db.driver()->formatValue(field);
+
+ // qDebug() << "preparedExec:" << QString("EXECUTE %1 (%2)").arg(queryId).arg(paramString);
+ return db.exec(QString("EXECUTE %1 (%2)").arg(queryId).arg(paramString));
+}
+
+void PostgreSqlStorage::deallocateQuery(const QString &handle, const QSqlDatabase &db) {
+ if(!_preparedQueries.contains(db.connectionName()) || !_preparedQueries[db.connectionName()].contains(handle)) {
+ return;
+ }
+ QMutexLocker locker(&_queryHashMutex);
+ QString queryId = _preparedQueries[db.connectionName()].take(handle);
+ db.exec(QString("DEALLOCATE %1").arg(queryId));
+}
+
+// ========================================
+// PostgreSqlMigrationWriter
+// ========================================
+PostgreSqlMigrationWriter::PostgreSqlMigrationWriter()
+ : PostgreSqlStorage()
+{
+}
+
+bool PostgreSqlMigrationWriter::prepareQuery(MigrationObject mo) {
+ QString query;
+ switch(mo) {
+ case QuasselUser:
+ query = queryString("migrate_write_quasseluser");
+ break;
+ case Sender:
+ query = queryString("migrate_write_sender");
+ break;
+ case Identity:
+ _validIdentities.clear();
+ query = queryString("migrate_write_identity");
+ break;
+ case IdentityNick:
+ query = queryString("migrate_write_identity_nick");
+ break;
+ case Network:
+ query = queryString("migrate_write_network");
+ break;
+ case Buffer:
+ query = queryString("migrate_write_buffer");
+ break;
+ case Backlog:
+ query = queryString("migrate_write_backlog");
+ break;
+ case IrcServer:
+ query = queryString("migrate_write_ircserver");
+ break;
+ case UserSetting:
+ query = queryString("migrate_write_usersetting");
+ break;
+ }
+ newQuery(query, logDb());
+ return true;
+}
+
+//bool PostgreSqlMigrationWriter::writeUser(const QuasselUserMO &user) {
+bool PostgreSqlMigrationWriter::writeMo(const QuasselUserMO &user) {
+ bindValue(0, user.id.toInt());
+ bindValue(1, user.username);
+ bindValue(2, user.password);
+ return exec();
+}
+
+//bool PostgreSqlMigrationWriter::writeSender(const SenderMO &sender) {
+bool PostgreSqlMigrationWriter::writeMo(const SenderMO &sender) {
+ bindValue(0, sender.senderId);
+ bindValue(1, sender.sender);
+ return exec();
+}
+
+//bool PostgreSqlMigrationWriter::writeIdentity(const IdentityMO &identity) {
+bool PostgreSqlMigrationWriter::writeMo(const IdentityMO &identity) {
+ _validIdentities << identity.id.toInt();
+ bindValue(0, identity.id.toInt());
+ bindValue(1, identity.userid.toInt());
+ bindValue(2, identity.identityname);
+ bindValue(3, identity.realname);
+ bindValue(4, identity.awayNick);
+ bindValue(5, identity.awayNickEnabled);
+ bindValue(6, identity.awayReason);
+ bindValue(7, identity.awayReasonEnabled);
+ bindValue(8, identity.autoAwayEnabled);
+ bindValue(9, identity.autoAwayTime);
+ bindValue(10, identity.autoAwayReason);
+ bindValue(11, identity.autoAwayReasonEnabled);
+ bindValue(12, identity.detachAwayEnabled);
+ bindValue(13, identity.detachAwayReason);
+ bindValue(14, identity.detchAwayReasonEnabled);
+ bindValue(15, identity.ident);
+ bindValue(16, identity.kickReason);
+ bindValue(17, identity.partReason);
+ bindValue(18, identity.quitReason);
+ bindValue(19, identity.sslCert);
+ bindValue(20, identity.sslKey);
+ return exec();
+}
+
+//bool PostgreSqlMigrationWriter::writeIdentityNick(const IdentityNickMO &identityNick) {
+bool PostgreSqlMigrationWriter::writeMo(const IdentityNickMO &identityNick) {
+ bindValue(0, identityNick.nickid);
+ bindValue(1, identityNick.identityId.toInt());
+ bindValue(2, identityNick.nick);
+ return exec();
+}
+
+//bool PostgreSqlMigrationWriter::writeNetwork(const NetworkMO &network) {
+bool PostgreSqlMigrationWriter::writeMo(const NetworkMO &network) {
+ bindValue(0, network.networkid.toInt());
+ bindValue(1, network.userid.toInt());
+ bindValue(2, network.networkname);
+ if(_validIdentities.contains(network.identityid.toInt()))
+ bindValue(3, network.identityid.toInt());
+ else
+ bindValue(3, QVariant());
+ bindValue(4, network.encodingcodec);
+ bindValue(5, network.decodingcodec);
+ bindValue(6, network.servercodec);
+ bindValue(7, network.userandomserver);
+ bindValue(8, network.perform);
+ bindValue(9, network.useautoidentify);
+ bindValue(10, network.autoidentifyservice);
+ bindValue(11, network.autoidentifypassword);
+ bindValue(12, network.useautoreconnect);
+ bindValue(13, network.autoreconnectinterval);
+ bindValue(14, network.autoreconnectretries);
+ bindValue(15, network.unlimitedconnectretries);
+ bindValue(16, network.rejoinchannels);
+ bindValue(17, network.connected);
+ bindValue(18, network.usermode);
+ bindValue(19, network.awaymessage);
+ bindValue(20, network.attachperform);
+ bindValue(21, network.detachperform);
+ return exec();
+}
+
+//bool PostgreSqlMigrationWriter::writeBuffer(const BufferMO &buffer) {
+bool PostgreSqlMigrationWriter::writeMo(const BufferMO &buffer) {
+ bindValue(0, buffer.bufferid.toInt());
+ bindValue(1, buffer.userid.toInt());
+ bindValue(2, buffer.groupid);
+ bindValue(3, buffer.networkid.toInt());
+ bindValue(4, buffer.buffername);
+ bindValue(5, buffer.buffercname);
+ bindValue(6, (int)buffer.buffertype);
+ bindValue(7, buffer.lastseenmsgid);
+ bindValue(8, buffer.key);
+ bindValue(9, buffer.joined);
+ return exec();
+}
+
+//bool PostgreSqlMigrationWriter::writeBacklog(const BacklogMO &backlog) {
+bool PostgreSqlMigrationWriter::writeMo(const BacklogMO &backlog) {
+ bindValue(0, backlog.messageid.toInt());
+ bindValue(1, backlog.time);
+ bindValue(2, backlog.bufferid.toInt());
+ bindValue(3, backlog.type);
+ bindValue(4, (int)backlog.flags);
+ bindValue(5, backlog.senderid);
+ bindValue(6, backlog.message);
+ return exec();
+}
+
+//bool PostgreSqlMigrationWriter::writeIrcServer(const IrcServerMO &ircserver) {
+bool PostgreSqlMigrationWriter::writeMo(const IrcServerMO &ircserver) {
+ bindValue(0, ircserver.serverid);
+ bindValue(1, ircserver.userid.toInt());
+ bindValue(2, ircserver.networkid.toInt());
+ bindValue(3, ircserver.hostname);
+ bindValue(4, ircserver.port);
+ bindValue(5, ircserver.password);
+ bindValue(6, ircserver.ssl);
+ bindValue(7, ircserver.sslversion);
+ bindValue(8, ircserver.useproxy);
+ bindValue(9, ircserver.proxytype);
+ bindValue(10, ircserver.proxyhost);
+ bindValue(11, ircserver.proxyport);
+ bindValue(12, ircserver.proxyuser);
+ bindValue(13, ircserver.proxypass);
+ return exec();
+}
+
+//bool PostgreSqlMigrationWriter::writeUserSetting(const UserSettingMO &userSetting) {
+bool PostgreSqlMigrationWriter::writeMo(const UserSettingMO &userSetting) {
+ bindValue(0, userSetting.userid.toInt());
+ bindValue(1, userSetting.settingname);
+ bindValue(2, userSetting.settingvalue);
+ return exec();
+}
+
+bool PostgreSqlMigrationWriter::postProcess() {
+ QSqlDatabase db = logDb();
+ QList<Sequence> sequences;
+ sequences << Sequence("backlog", "messageid")
+ << Sequence("buffer", "bufferid")
+ << Sequence("identity", "identityid")
+ << Sequence("identity_nick", "nickid")
+ << Sequence("ircserver", "serverid")
+ << Sequence("network", "networkid")
+ << Sequence("quasseluser", "userid")
+ << Sequence("sender", "senderid");
+ QList<Sequence>::const_iterator iter;
+ for(iter = sequences.constBegin(); iter != sequences.constEnd(); iter++) {
+ resetQuery();
+ newQuery(QString("SELECT setval('%1_%2_seq', max(%2)) FROM %1").arg(iter->table, iter->field), db);
+ if(!exec())
+ return false;
+ }
+ return true;
+}