- 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;
- QDir dir = QDir(QString(":/SQL/%1/%2/").arg(displayName()).arg(schemaVersion()));
- foreach(QFileInfo fileInfo, dir.entryInfoList(QStringList() << "setup*", QDir::NoFilter, QDir::Name)) {
- queries << queryString(fileInfo.baseName());
- }
- return queries;
-}
-
-bool AbstractSqlStorage::setup(const QVariantMap &settings) {
- Q_UNUSED(settings)
- QSqlDatabase db = logDb();
- if(!db.isOpen()) {
- qCritical() << "Unable to setup Logging Backend!";
- return false;
- }
-
- foreach(QString queryString, setupQueries()) {
- QSqlQuery query = db.exec(queryString);
- if(!watchQuery(query)) {
- qCritical() << "Unable to setup Logging Backend!";
- return false;
- }
- }
- return true;
-}
-
-QStringList AbstractSqlStorage::upgradeQueries(int version) {
- QStringList queries;
- QDir dir = QDir(QString(":/SQL/%1/%2/").arg(displayName()).arg(version));
- foreach(QFileInfo fileInfo, dir.entryInfoList(QStringList() << "upgrade*", QDir::NoFilter, QDir::Name)) {
- queries << queryString(fileInfo.baseName(), version);
- }
- return queries;
-}
-
-bool AbstractSqlStorage::upgradeDb() {
- if(schemaVersion() <= installedSchemaVersion())
- return true;
+int AbstractSqlStorage::_nextConnectionId = 0;
+AbstractSqlStorage::AbstractSqlStorage(QObject* parent)
+ : Storage(parent)
+{}
+
+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(), nullptr, this, nullptr);
+ }
+}
+
+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, &QObject::destroyed, connection, &QObject::deleteLater);
+ connect(currentThread, &QObject::destroyed, connection, &QObject::deleteLater);
+ connect(connection, &QObject::destroyed, this, &AbstractSqlStorage::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()) {
+ qWarning() << "Unable to open database" << displayName() << "for thread" << QThread::currentThread();
+ qWarning() << "-" << db.lastError().text();
+ }
+ else {
+ if (!initDbSession(db)) {
+ qWarning() << "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()) {
+ qInfo() << 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.
+ qInfo() << qPrintable(tr("Installed database schema successfully upgraded to version %1.").arg(schemaVersion()));
+ }
+
+ qInfo() << 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();