core: Workaround Qt 4 SQL bindValue() duplicates
[quassel.git] / src / core / postgresqlstorage.cpp
index 48236bc..9ed72e3 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2005-2018 by the Quassel Project                        *
+ *   Copyright (C) 2005-2019 by the Quassel Project                        *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
@@ -22,7 +22,7 @@
 
 #include <QtSql>
 
-#include "logger.h"
+#include "logmessage.h"
 #include "network.h"
 #include "quassel.h"
 
@@ -187,19 +187,39 @@ int PostgreSqlStorage::installedSchemaVersion()
 }
 
 
-bool PostgreSqlStorage::updateSchemaVersion(int newVersion)
+bool PostgreSqlStorage::updateSchemaVersion(int newVersion, bool clearUpgradeStep)
 {
-    QSqlQuery query(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.
+    QSqlDatabase db = logDb();
+    if (!beginTransaction(db)) {
+        qWarning() << "PostgreSqlStorage::updateSchemaVersion(int, bool): cannot start transaction!";
+        qWarning() << " -" << qPrintable(db.lastError().text());
+        return false;
+    }
+
+    QSqlQuery query(db);
     query.prepare("UPDATE coreinfo SET value = :version WHERE key = 'schemaversion'");
     query.bindValue(":version", newVersion);
     safeExec(query);
 
-    bool success = true;
     if (!watchQuery(query)) {
-        qCritical() << "PostgreSqlStorage::updateSchemaVersion(int): Updating schema version failed!";
-        success = false;
+        qCritical() << "PostgreSqlStorage::updateSchemaVersion(int, bool): Updating schema version failed!";
+        db.rollback();
+        return false;
     }
-    return success;
+
+    if (clearUpgradeStep) {
+        // Try clearing the upgrade step if requested
+        if (!setSchemaVersionUpgradeStep("")) {
+            db.rollback();
+            return false;
+        }
+    }
+
+    // Successful, commit and return true
+    db.commit();
+    return true;
 }
 
 
@@ -219,6 +239,52 @@ bool PostgreSqlStorage::setupSchemaVersion(int version)
 }
 
 
+QString PostgreSqlStorage::schemaVersionUpgradeStep()
+{
+    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 PostgreSqlStorage::setSchemaVersionUpgradeStep(QString upgradeQuery)
+{
+    // 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);
+
+    // Make sure that the query didn't fail (shouldn't ever happen), and that some non-zero number
+    // of rows were affected
+    bool success = watchQuery(query) && 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 PostgreSqlStorage::addUser(const QString &user, const QString &password, const QString &authenticator)
 {
     QSqlQuery query(logDb());
@@ -1634,6 +1700,8 @@ bool PostgreSqlStorage::logMessage(Message &msg)
     }
 
     QVariantList params;
+    // PostgreSQL handles QDateTime()'s serialized format by default, and QDateTime() serializes
+    // to a 64-bit time compatible format by default.
     params << msg.timestamp()
            << msg.bufferInfo().bufferId().toInt()
            << msg.type()
@@ -1718,6 +1786,8 @@ bool PostgreSqlStorage::logMessages(MessageList &msgs)
     for (int i = 0; i < msgs.count(); i++) {
         Message &msg = msgs[i];
         QVariantList params;
+        // PostgreSQL handles QDateTime()'s serialized format by default, and QDateTime() serializes
+        // to a 64-bit time compatible format by default.
         params << msg.timestamp()
                << msg.bufferInfo().bufferId().toInt()
                << msg.type()
@@ -1797,17 +1867,19 @@ QList<Message> PostgreSqlStorage::requestMsgs(UserId user, BufferId bufferId, Ms
 
     QDateTime timestamp;
     while (query.next()) {
+        // PostgreSQL returns date/time in ISO 8601 format, no 64-bit handling needed
+        // See https://www.postgresql.org/docs/current/static/datatype-datetime.html#DATATYPE-DATETIME-OUTPUT
         timestamp = query.value(1).toDateTime();
         timestamp.setTimeSpec(Qt::UTC);
         Message msg(timestamp,
             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).toUInt());
+            (Message::Flags)query.value(3).toInt());
         msg.setMsgId(query.value(0).toLongLong());
         messagelist << msg;
     }
@@ -1837,13 +1909,18 @@ QList<Message> PostgreSqlStorage::requestMsgsFiltered(UserId user, BufferId buff
     QSqlQuery query(db);
     if (last == -1 && first == -1) {
         query.prepare(queryString("select_messagesNewestK_filtered"));
+        // Workaround for Qt 4 QSqlQuery::bindValue() not supporting repeated placeholder names
+        query.bindValue(":bufferDup1", bufferId.toInt());
     } else if (last == -1) {
         query.prepare(queryString("select_messagesNewerThan_filtered"));
         query.bindValue(":first", first.toQint64());
+        // Workaround for Qt 4 QSqlQuery::bindValue() not supporting repeated placeholder names
+        query.bindValue(":bufferDup1", bufferId.toInt());
     } else {
         query.prepare(queryString("select_messagesRange_filtered"));
         query.bindValue(":last", last.toQint64());
         query.bindValue(":first", first.toQint64());
+        // Workaround for Qt 4 QSqlQuery::bindValue() not needed, only has one ":buffer"
     }
     query.bindValue(":buffer", bufferId.toInt());
     query.bindValue(":limit", limit);
@@ -1851,6 +1928,8 @@ QList<Message> PostgreSqlStorage::requestMsgsFiltered(UserId user, BufferId buff
     query.bindValue(":type", typeRaw);
     int flagsRaw = flags;
     query.bindValue(":flags", flagsRaw);
+    // Workaround for Qt 4 QSqlQuery::bindValue() not supporting repeated placeholder names
+    query.bindValue(":flagsDup1", flagsRaw);
 
     safeExec(query);
     if (!watchQuery(query)) {
@@ -1861,11 +1940,13 @@ QList<Message> PostgreSqlStorage::requestMsgsFiltered(UserId user, BufferId buff
 
     QDateTime timestamp;
     while (query.next()) {
+        // PostgreSQL returns date/time in ISO 8601 format, no 64-bit handling needed
+        // See https://www.postgresql.org/docs/current/static/datatype-datetime.html#DATATYPE-DATETIME-OUTPUT
         timestamp = query.value(1).toDateTime();
         timestamp.setTimeSpec(Qt::UTC);
         Message msg(timestamp,
                     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(),
@@ -1916,17 +1997,19 @@ QList<Message> PostgreSqlStorage::requestAllMsgs(UserId user, MsgId first, MsgId
 
     QDateTime timestamp;
     for (int i = 0; i < limit && query.next(); i++) {
+        // PostgreSQL returns date/time in ISO 8601 format, no 64-bit handling needed
+        // See https://www.postgresql.org/docs/current/static/datatype-datetime.html#DATATYPE-DATETIME-OUTPUT
         timestamp = query.value(2).toDateTime();
         timestamp.setTimeSpec(Qt::UTC);
         Message msg(timestamp,
             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).toUInt());
+            (Message::Flags)query.value(4).toInt());
         msg.setMsgId(query.value(0).toLongLong());
         messagelist << msg;
     }
@@ -1969,6 +2052,8 @@ QList<Message> PostgreSqlStorage::requestAllMsgsFiltered(UserId user, MsgId firs
 
     int flagsRaw = flags;
     query.bindValue(":flags", flagsRaw);
+    // Workaround for Qt 4 QSqlQuery::bindValue() not supporting repeated placeholder names
+    query.bindValue(":flagsDup1", flagsRaw);
 
     safeExec(query);
     if (!watchQuery(query)) {
@@ -1978,11 +2063,13 @@ QList<Message> PostgreSqlStorage::requestAllMsgsFiltered(UserId user, MsgId firs
 
     QDateTime timestamp;
     for (int i = 0; i < limit && query.next(); i++) {
+        // PostgreSQL returns date/time in ISO 8601 format, no 64-bit handling needed
+        // See https://www.postgresql.org/docs/current/static/datatype-datetime.html#DATATYPE-DATETIME-OUTPUT
         timestamp = query.value(2).toDateTime();
         timestamp.setTimeSpec(Qt::UTC);
         Message msg(timestamp,
                     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(),
@@ -2012,21 +2099,6 @@ QMap<UserId, QString> PostgreSqlStorage::getAllAuthUserNames()
 }
 
 
-QString PostgreSqlStorage::getAuthUserName(UserId user)
-{
-    QString authusername;
-    QSqlQuery query(logDb());
-    query.prepare(queryString("select_authusername"));
-    query.bindValue(":userid", user.toInt());
-    safeExec(query);
-    watchQuery(query);
-
-    if (query.first()) {
-         authusername = query.value(0).toString();
-    }
-    return authusername;
-}
-
 // void PostgreSqlStorage::safeExec(QSqlQuery &query) {
 //   qDebug() << "PostgreSqlStorage::safeExec";
 //   qDebug() << "   executing:\n" << query.executedQuery();