+AbstractSqlStorage::~AbstractSqlStorage()
+{
+ // disconnect the connections, so their deletion is no longer interessting for us
+ QHash<QThread *, Connection *>::iterator conIter;
+ for (conIter = _connectionPool.begin(); conIter != _connectionPool.end(); ++conIter) {
+ QSqlDatabase::removeDatabase(conIter.value()->name());
+ disconnect(conIter.value(), 0, this, 0);
+ }
+}
+
+
+QSqlDatabase AbstractSqlStorage::logDb()
+{
+ if (!_connectionPool.contains(QThread::currentThread()))
+ addConnectionToPool();
+
+ QSqlDatabase db = QSqlDatabase::database(_connectionPool[QThread::currentThread()]->name(),false);
+
+ if (!db.isOpen()) {
+ qWarning() << "Database connection" << displayName() << "for thread" << QThread::currentThread() << "was lost, attempting to reconnect...";
+ dbConnect(db);
+ }
+
+ return db;
+}
+
+
+void AbstractSqlStorage::addConnectionToPool()
+{
+ QMutexLocker locker(&_connectionPoolMutex);
+ // we have to recheck if the connection pool already contains a connection for
+ // this thread. Since now (after the lock) we can only tell for sure
+ if (_connectionPool.contains(QThread::currentThread()))
+ return;
+
+ QThread *currentThread = QThread::currentThread();
+
+ int connectionId = _nextConnectionId++;
+
+ 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()));
+ connect(connection, SIGNAL(destroyed()), this, SLOT(connectionDestroyed()));
+ _connectionPool[currentThread] = connection;
+
+ QSqlDatabase db = QSqlDatabase::addDatabase(driverName(), connection->name());
+ db.setDatabaseName(databaseName());
+
+ if (!hostName().isEmpty())
+ db.setHostName(hostName());
+
+ if (port() != -1)
+ db.setPort(port());
+
+ if (!userName().isEmpty()) {
+ db.setUserName(userName());
+ db.setPassword(password());
+ }
+
+ dbConnect(db);
+}
+
+
+void AbstractSqlStorage::dbConnect(QSqlDatabase &db)
+{
+ if (!db.open()) {
+ quWarning() << "Unable to open database" << displayName() << "for thread" << QThread::currentThread();
+ quWarning() << "-" << db.lastError().text();
+ }
+ else {
+ if (!initDbSession(db)) {
+ quWarning() << "Unable to initialize database" << displayName() << "for thread" << QThread::currentThread();
+ db.close();
+ }
+ }
+}
+
+
+Storage::State AbstractSqlStorage::init(const QVariantMap &settings,
+ const QProcessEnvironment &environment,
+ bool loadFromEnvironment)
+{
+ setConnectionProperties(settings, environment, loadFromEnvironment);
+
+ _debug = Quassel::isOptionSet("debug");
+
+ QSqlDatabase db = logDb();
+ if (!db.isValid() || !db.isOpen())
+ return NotAvailable;
+
+ if (installedSchemaVersion() == -1) {
+ qCritical() << "Storage Schema is missing!";
+ return NeedsSetup;
+ }
+
+ if (installedSchemaVersion() > schemaVersion()) {
+ qCritical() << "Installed Schema is newer then any known Version.";
+ return NotAvailable;
+ }
+
+ if (installedSchemaVersion() < schemaVersion()) {
+ quInfo() << qPrintable(tr("Installed database schema (version %1) is not up to date. Upgrading to "
+ "version %2... This may take a while for major upgrades."
+ ).arg(installedSchemaVersion()).arg(schemaVersion()));
+ emit dbUpgradeInProgress(true);
+ auto upgradeResult = upgradeDb();
+ emit dbUpgradeInProgress(false);
+ if (!upgradeResult) {
+ qWarning() << qPrintable(tr("Upgrade failed..."));
+ return NotAvailable;
+ }
+ // Add a message when migration succeeds to avoid confusing folks by implying the schema upgrade failed if
+ // later functionality does not work.
+ quInfo() << qPrintable(tr("Installed database schema successfully upgraded to version %1.").arg(schemaVersion()));
+ }
+
+ quInfo() << qPrintable(displayName()) << "storage backend is ready. Schema version:" << installedSchemaVersion();
+ return IsReady;
+}
+
+
+QString AbstractSqlStorage::queryString(const QString &queryName, int version)
+{
+ QFileInfo queryInfo;
+
+ // The current schema is stored in the root folder, while upgrade queries are stored in the
+ // 'versions/##' subfolders.
+ if (version == 0) {
+ // Use the current SQL schema, not a versioned request
+ queryInfo = QFileInfo(QString(":/SQL/%1/%2.sql").arg(displayName()).arg(queryName));
+ // If version is needed later, get it via version = schemaVersion();
+ } else {
+ // Use the specified schema version, not the general folder
+ queryInfo = QFileInfo(QString(":/SQL/%1/version/%2/%3.sql")
+ .arg(displayName()).arg(version).arg(queryName));
+ }
+
+ if (!queryInfo.exists() || !queryInfo.isFile() || !queryInfo.isReadable()) {
+ qCritical() << "Unable to read SQL-Query" << queryName << "for engine" << displayName();
+ return QString();
+ }
+
+ QFile queryFile(queryInfo.filePath());
+ if (!queryFile.open(QIODevice::ReadOnly | QIODevice::Text))
+ return QString();
+ QString query = QTextStream(&queryFile).readAll();
+ queryFile.close();
+
+ return query.trimmed();
+}
+
+
+QStringList AbstractSqlStorage::setupQueries()
+{
+ QStringList queries;
+ // The current schema is stored in the root folder, including setup scripts.
+ QDir dir = QDir(QString(":/SQL/%1/").arg(displayName()));
+ foreach(QFileInfo fileInfo, dir.entryInfoList(QStringList() << "setup*", QDir::NoFilter, QDir::Name)) {
+ queries << queryString(fileInfo.baseName());
+ }
+ return queries;
+}
+
+
+bool AbstractSqlStorage::setup(const QVariantMap &settings, const QProcessEnvironment &environment,
+ bool loadFromEnvironment)
+{
+ setConnectionProperties(settings, environment, loadFromEnvironment);
+ QSqlDatabase db = logDb();
+ if (!db.isOpen()) {
+ qCritical() << "Unable to setup Logging Backend!";
+ return false;
+ }
+
+ db.transaction();
+ foreach(QString queryString, setupQueries()) {
+ QSqlQuery query = db.exec(queryString);
+ if (!watchQuery(query)) {
+ qCritical() << "Unable to setup Logging Backend!";
+ db.rollback();
+ return false;
+ }
+ }
+ bool success = setupSchemaVersion(schemaVersion());
+ if (success)
+ db.commit();
+ else
+ db.rollback();
+ return success;
+}
+
+
+QStringList AbstractSqlStorage::upgradeQueries(int version)
+{
+ QStringList queries;
+ // Upgrade queries are stored in the 'version/##' subfolders.
+ QDir dir = QDir(QString(":/SQL/%1/version/%2/").arg(displayName()).arg(version));
+ foreach(QFileInfo fileInfo, dir.entryInfoList(QStringList() << "upgrade*", QDir::NoFilter, QDir::Name)) {
+ queries << queryString(fileInfo.baseName(), version);
+ }
+ return queries;
+}
+