first version of backend migration interface
authorMarcus Eggenberger <egs@quassel-irc.org>
Sat, 28 Feb 2009 17:58:10 +0000 (18:58 +0100)
committerMarcus Eggenberger <egs@quassel-irc.org>
Tue, 3 Mar 2009 19:57:04 +0000 (20:57 +0100)
src/core/abstractsqlstorage.cpp
src/core/abstractsqlstorage.h

index fa230f3..3d339ec 100644 (file)
@@ -59,7 +59,7 @@ void AbstractSqlStorage::addConnectionToPool() {
 
   int connectionId = _nextConnectionId++;
 
 
   int connectionId = _nextConnectionId++;
 
-  Connection *connection = new Connection(QLatin1String(QString("quassel_connection_%1").arg(connectionId).toLatin1()));
+  Connection *connection = new Connection(QLatin1String(QString("quassel_%1_con_%2").arg(driverName()).arg(connectionId).toLatin1()));
   connection->moveToThread(currentThread);
   connect(this, SIGNAL(destroyed()), connection, SLOT(deleteLater()));
   connect(currentThread, SIGNAL(destroyed()), connection, SLOT(deleteLater()));
   connection->moveToThread(currentThread);
   connect(this, SIGNAL(destroyed()), connection, SLOT(deleteLater()));
   connect(currentThread, SIGNAL(destroyed()), connection, SLOT(deleteLater()));
@@ -262,3 +262,213 @@ AbstractSqlStorage::Connection::~Connection() {
   }
   QSqlDatabase::removeDatabase(name());
 }
   }
   QSqlDatabase::removeDatabase(name());
 }
+
+
+
+
+// ========================================
+//  AbstractSqlMigrator
+// ========================================
+AbstractSqlMigrator::AbstractSqlMigrator()
+  : _query(0)
+{
+}
+
+void AbstractSqlMigrator::newQuery(const QString &query, QSqlDatabase db) {
+  Q_ASSERT(!_query);
+  _query = new QSqlQuery(db);
+  _query->prepare(query);
+}
+
+void AbstractSqlMigrator::resetQuery() {
+  delete _query;
+  _query = 0;
+}
+
+bool AbstractSqlMigrator::exec() {
+  Q_ASSERT(_query);
+  _query->exec();
+  return !_query->lastError().isValid();
+}
+
+QString AbstractSqlMigrator::migrationObject(MigrationObject moType) {
+  switch(moType) {
+  case QuasselUser:
+    return "QuasselUser";
+  case Sender:
+    return "Sender";
+  case Identity:
+    return "Identity";
+  case IdentityNick:
+    return "IdentityNick";
+  case Network:
+    return "Network";
+  case Buffer:
+    return "Buffer";
+  case Backlog:
+    return "Backlog";
+  case IrcServer:
+    return "IrcServer";
+  case UserSetting:
+    return "UserSetting";
+  };
+  return QString();
+}
+
+QVariantList AbstractSqlMigrator::boundValues() {
+  QVariantList values;
+  if(!_query)
+    return values;
+
+  int numValues = _query->boundValues().count();
+  for(int i = 0; i < numValues; i++) {
+    values << _query->boundValue(i);
+  }
+  return values;
+}
+
+void AbstractSqlMigrator::dumpStatus() {
+  qWarning() << "  executed Query:";
+  qWarning() << qPrintable(executedQuery());
+  qWarning() << "  bound Values:";
+  QList<QVariant> list = boundValues();
+  for (int i = 0; i < list.size(); ++i)
+    qWarning() << i << ": " << list.at(i).toString().toAscii().data();
+  qWarning() << "  Error Number:"   << lastError().number();
+  qWarning() << "  Error Message:"   << lastError().text();
+}
+
+
+// ========================================
+//  AbstractSqlMigrationReader
+// ========================================
+AbstractSqlMigrationReader::AbstractSqlMigrationReader()
+  : AbstractSqlMigrator(),
+    _writer(0)
+{
+}
+
+bool AbstractSqlMigrationReader::migrateTo(AbstractSqlMigrationWriter *writer) {
+  if(!transaction()) {
+    qWarning() << "AbstractSqlMigrationReader::migrateTo(): unable to start reader stransaction!";
+    return false;
+  }
+  if(!writer->transaction()) {
+    qWarning() << "AbstractSqlMigrationReader::migrateTo(): unable to start writer stransaction!";
+    rollback(); // close the reader transaction;
+    return false;
+  }
+
+  _writer = writer;
+
+  // due to the incompatibility across Migration objects we can't run this in a loop... :/
+  QuasselUserMO quasselUserMo;
+  if(!transferMo(QuasselUser, quasselUserMo))
+     return false;
+
+  SenderMO senderMo;
+  if(!transferMo(Sender, senderMo))
+    return false;
+
+  IdentityMO identityMo;
+  if(!transferMo(Identity, identityMo))
+    return false;
+
+  IdentityNickMO identityNickMo;
+  if(!transferMo(IdentityNick, identityNickMo))
+    return false;
+
+  NetworkMO networkMo;
+  if(!transferMo(Network, networkMo))
+    return false;
+
+  BufferMO bufferMo;
+  if(!transferMo(Buffer, bufferMo))
+    return false;
+
+  BacklogMO backlogMo;
+  if(!transferMo(Backlog, backlogMo))
+    return false;
+
+  IrcServerMO ircServerMo;
+  if(!transferMo(IrcServer, ircServerMo))
+    return false;
+
+  UserSettingMO userSettingMo;
+  if(!transferMo(UserSetting, userSettingMo))
+    return false;
+
+  return finalizeMigration();
+}
+
+void AbstractSqlMigrationReader::abortMigration(const QString &errorMsg) {
+  qWarning() << "Migration Failed!";
+  if(!errorMsg.isNull()) {
+    qWarning() << qPrintable(errorMsg);
+  }
+  if(lastError().isValid()) {
+    qWarning() << "ReaderError:";
+    dumpStatus();
+  }
+
+
+  if(_writer->lastError().isValid()) {
+    qWarning() << "WriterError:";
+    _writer->dumpStatus();
+  }
+
+  rollback();
+  _writer->rollback();
+  _writer = 0;
+}
+
+bool AbstractSqlMigrationReader::finalizeMigration() {
+  resetQuery();
+  _writer->resetQuery();
+
+  commit();
+  if(!_writer->commit()) {
+    _writer = 0;
+    return false;
+  }
+  _writer = 0;
+  return true;
+}
+
+template<typename T>
+bool AbstractSqlMigrationReader::transferMo(MigrationObject moType, T &mo) {
+  resetQuery();
+  _writer->resetQuery();
+
+  if(!prepareQuery(moType)) {
+    abortMigration(QString("AbstractSqlMigrationReader::migrateTo(): unable to prepare reader query of type %1!").arg(AbstractSqlMigrator::migrationObject(moType)));
+    return false;
+  }
+  if(!_writer->prepareQuery(moType)) {
+    abortMigration(QString("AbstractSqlMigrationReader::migrateTo(): unable to prepare writer query of type %1!").arg(AbstractSqlMigrator::migrationObject(moType)));
+    return false;
+  }
+
+  qDebug() << qPrintable(QString("Transfering %1...").arg(AbstractSqlMigrator::migrationObject(moType)));
+  int i = 0;
+  QFile file;
+  file.open(stdout, QIODevice::WriteOnly);
+  while(readMo(mo)) {
+    if(!_writer->writeMo(mo)) {
+      abortMigration(QString("AbstractSqlMigrationReader::transferMo(): unable to transfer Migratable Object of type %1!").arg(AbstractSqlMigrator::migrationObject(moType)));
+      return false;
+    }
+    i++;
+    if(i % 1000 == 0) {
+      file.write("*");
+      file.flush();
+    }
+  }
+  if(i > 1000) {
+    file.write("\n");
+    file.flush();
+  }
+  qDebug() << "Done.";
+  return true;
+}
+
index e087c6d..e14abd9 100644 (file)
@@ -24,8 +24,8 @@
 #include "storage.h"
 
 #include <QSqlDatabase>
 #include "storage.h"
 
 #include <QSqlDatabase>
-
-class QSqlQuery;
+#include <QSqlQuery>
+#include <QSqlError>
 
 class AbstractSqlStorage : public Storage {
   Q_OBJECT
 
 class AbstractSqlStorage : public Storage {
   Q_OBJECT
@@ -34,12 +34,13 @@ public:
   AbstractSqlStorage(QObject *parent = 0);
   virtual ~AbstractSqlStorage();
 
   AbstractSqlStorage(QObject *parent = 0);
   virtual ~AbstractSqlStorage();
 
-protected:
   virtual State init(const QVariantMap &settings = QVariantMap());
   virtual State init(const QVariantMap &settings = QVariantMap());
+
+protected:
   inline virtual void sync() {};
   inline virtual void sync() {};
-  
+
   QSqlDatabase logDb();
   QSqlDatabase logDb();
-  
+
   QString queryString(const QString &queryName, int version);
   inline QString queryString(const QString &queryName) { return queryString(queryName, 0); }
 
   QString queryString(const QString &queryName, int version);
   inline QString queryString(const QString &queryName) { return queryString(queryName, 0); }
 
@@ -50,7 +51,7 @@ protected:
   bool upgradeDb();
 
   bool watchQuery(QSqlQuery &query);
   bool upgradeDb();
 
   bool watchQuery(QSqlQuery &query);
-  
+
   int schemaVersion();
   virtual int installedSchemaVersion() { return -1; };
   virtual bool updateSchemaVersion(int newVersion) = 0;
   int schemaVersion();
   virtual int installedSchemaVersion() { return -1; };
   virtual bool updateSchemaVersion(int newVersion) = 0;
@@ -90,11 +91,213 @@ class AbstractSqlStorage::Connection : public QObject {
 public:
   Connection(const QString &name, QObject *parent = 0);
   ~Connection();
 public:
   Connection(const QString &name, QObject *parent = 0);
   ~Connection();
-  
+
   inline QLatin1String name() const { return QLatin1String(_name); }
 
 private:
   QByteArray _name;
 };
 
   inline QLatin1String name() const { return QLatin1String(_name); }
 
 private:
   QByteArray _name;
 };
 
+
+// ========================================
+//  AbstractSqlMigrator
+// ========================================
+class AbstractSqlMigrator {
+public:
+  // migration objects
+  struct QuasselUserMO {
+    UserId id;
+    QString username;
+    QString password;
+  };
+
+  struct SenderMO {
+    int senderId;
+    QString sender;
+  };
+
+  struct IdentityMO {
+    IdentityId id;
+    UserId userid;
+    QString identityname;
+    QString realname;
+    QString awayNick;
+    bool awayNickEnabled;
+    QString awayReason;
+    bool awayReasonEnabled;
+    bool autoAwayEnabled;
+    int autoAwayTime;
+    QString autoAwayReason;
+    bool autoAwayReasonEnabled;
+    bool detachAwayEnabled;
+    QString detachAwayReason;
+    bool detchAwayReasonEnabled;
+    QString ident;
+    QString kickReason;
+    QString partReason;
+    QString quitReason;
+    QByteArray sslCert;
+    QByteArray sslKey;
+  };
+
+  struct IdentityNickMO {
+    int nickid;
+    IdentityId identityId;
+    QString nick;
+  };
+
+  struct NetworkMO {
+    NetworkId networkid;
+    UserId userid;
+    QString networkname;
+    IdentityId identityid;
+    QString encodingcodec;
+    QString decodingcodec;
+    QString servercodec;
+    bool userandomserver;
+    QString perform;
+    bool useautoidentify;
+    QString autoidentifyservice;
+    QString autoidentifypassword;
+    bool useautoreconnect;
+    int autoreconnectinterval;
+    int autoreconnectretries;
+    bool unlimitedconnectretries;
+    bool rejoinchannels;
+    bool connected;
+    QString usermode;
+    QString awaymessage;
+    QString attachperform;
+    QString detachperform;
+  };
+
+  struct BufferMO {
+    BufferId bufferid;
+    UserId userid;
+    int groupid;
+    NetworkId networkid;
+    QString buffername;
+    QString buffercname;
+    int buffertype;
+    int lastseenmsgid;
+    QString key;
+    bool joined;
+  };
+
+  struct BacklogMO {
+    MsgId messageid;
+    QDateTime time; // has to be in UTC!
+    BufferId bufferid;
+    int type;
+    int flags;
+    int senderid;
+    QString message;
+  };
+
+  struct IrcServerMO {
+    int serverid;
+    UserId userid;
+    NetworkId networkid;
+    QString hostname;
+    int port;
+    QString password;
+    bool ssl;
+    int sslversion;
+    bool useproxy;
+    int proxytype;
+    QString proxyhost;
+    int proxyport;
+    QString proxyuser;
+    QString proxypass;
+  };
+
+  struct UserSettingMO {
+    UserId userid;
+    QString settingname;
+    QByteArray settingvalue;
+  };
+
+  enum MigrationObject {
+    QuasselUser,
+    Sender,
+    Identity,
+    IdentityNick,
+    Network,
+    Buffer,
+    Backlog,
+    IrcServer,
+    UserSetting
+  };
+
+  AbstractSqlMigrator();
+  virtual ~AbstractSqlMigrator() {}
+
+  static QString migrationObject(MigrationObject moType);
+
+protected:
+  void newQuery(const QString &query, QSqlDatabase db);
+  virtual void resetQuery();
+  virtual bool prepareQuery(MigrationObject mo) = 0;
+  bool exec();
+  inline bool next() { return _query->next(); }
+  inline QVariant value(int index) { return _query->value(index); }
+  inline void bindValue(const QString &placeholder, const QVariant &val) { _query->bindValue(placeholder, val); }
+  inline void bindValue(int pos, const QVariant &val) { _query->bindValue(pos, val); }
+
+  inline QSqlError lastError() { return _query ? _query->lastError() : QSqlError(); }
+  void dumpStatus();
+  inline QString executedQuery() { return _query ? _query->executedQuery() : QString(); }
+  inline QVariantList boundValues();
+
+  virtual bool transaction() = 0;
+  virtual void rollback() = 0;
+  virtual bool commit() = 0;
+
+private:
+  QSqlQuery *_query;
+};
+
+class AbstractSqlMigrationWriter;
+class AbstractSqlMigrationReader : public AbstractSqlMigrator {
+public:
+  AbstractSqlMigrationReader();
+
+  virtual bool readMo(QuasselUserMO &user) = 0;
+  virtual bool readMo(SenderMO &sender) = 0;
+  virtual bool readMo(IdentityMO &identity) = 0;
+  virtual bool readMo(IdentityNickMO &identityNick) = 0;
+  virtual bool readMo(NetworkMO &network) = 0;
+  virtual bool readMo(BufferMO &buffer) = 0;
+  virtual bool readMo(BacklogMO &backlog) = 0;
+  virtual bool readMo(IrcServerMO &ircserver) = 0;
+  virtual bool readMo(UserSettingMO &userSetting) = 0;
+
+  bool migrateTo(AbstractSqlMigrationWriter *writer);
+
+private:
+  void abortMigration(const QString &errorMsg = QString());
+  bool finalizeMigration();
+
+  template<typename T> bool transferMo(MigrationObject moType, T &mo);
+
+  AbstractSqlMigrationWriter *_writer;
+};
+
+class AbstractSqlMigrationWriter : public AbstractSqlMigrator {
+public:
+  virtual bool writeMo(const QuasselUserMO &user) = 0;
+  virtual bool writeMo(const SenderMO &sender) = 0;
+  virtual bool writeMo(const IdentityMO &identity) = 0;
+  virtual bool writeMo(const IdentityNickMO &identityNick) = 0;
+  virtual bool writeMo(const NetworkMO &network) = 0;
+  virtual bool writeMo(const BufferMO &buffer) = 0;
+  virtual bool writeMo(const BacklogMO &backlog) = 0;
+  virtual bool writeMo(const IrcServerMO &ircserver) = 0;
+  virtual bool writeMo(const UserSettingMO &userSetting) = 0;
+
+  inline bool migrateFrom(AbstractSqlMigrationReader *reader) { return reader->migrateTo(this); }
+
+  friend class AbstractSqlMigrationReader;
+};
+
 #endif
 #endif