File indexing completed on 2024-05-12 16:25:42

0001 /*
0002    SPDX-FileCopyrightText: 2023-2024 Laurent Montel <montel.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "localdatabasebase.h"
0008 #include "localdatabaseutils.h"
0009 #include "ruqola_database_debug.h"
0010 
0011 #include <QDir>
0012 #include <QFileInfo>
0013 #include <QSqlDatabase>
0014 #include <QSqlError>
0015 #include <QSqlQuery>
0016 
0017 LocalDatabaseBase::LocalDatabaseBase(const QString &basePath, LocalDatabaseBase::DatabaseType type)
0018     : mBasePath(basePath)
0019     , mDatabaseType(type)
0020 {
0021 }
0022 
0023 LocalDatabaseBase::~LocalDatabaseBase() = default;
0024 
0025 QString LocalDatabaseBase::dbFileName(const QString &accountName, const QString &roomName) const
0026 {
0027     const QString dirPath = mBasePath + accountName;
0028     return dirPath + QLatin1Char('/') + roomName + QStringLiteral(".sqlite");
0029 }
0030 
0031 QString LocalDatabaseBase::dbFileName(const QString &accountName) const
0032 {
0033     const QString dirPath = mBasePath + accountName;
0034     return dirPath + QLatin1Char('/') + accountName + QStringLiteral(".sqlite");
0035 }
0036 
0037 QString LocalDatabaseBase::schemaDatabaseStr() const
0038 {
0039     return schemaDataBase();
0040 }
0041 
0042 QString LocalDatabaseBase::schemaDataBase() const
0043 {
0044     Q_ASSERT(false);
0045     return {};
0046 }
0047 
0048 QString LocalDatabaseBase::databaseName(const QString &name) const
0049 {
0050     QString prefix;
0051     switch (mDatabaseType) {
0052     case DatabaseType::Unknown:
0053         qCWarning(RUQOLA_DATABASE_LOG) << "Unknown data base it's a bug";
0054         break;
0055     case DatabaseType::Account:
0056         prefix = QStringLiteral("accounts-");
0057         break;
0058     case DatabaseType::Rooms:
0059         prefix = QStringLiteral("rooms-");
0060         break;
0061     case DatabaseType::Message:
0062         prefix = QStringLiteral("messages-");
0063         break;
0064     case DatabaseType::Global:
0065         prefix = QStringLiteral("global-");
0066         break;
0067     case DatabaseType::Logger:
0068         break;
0069     }
0070     return prefix + name;
0071 }
0072 
0073 bool LocalDatabaseBase::checkDataBase(const QString &accountName, const QString &_roomName, QSqlDatabase &db)
0074 {
0075     const QString roomName = LocalDatabaseUtils::fixRoomName(_roomName);
0076     const QString dbName = databaseName(accountName + QLatin1Char('-') + roomName);
0077     db = QSqlDatabase::database(dbName);
0078     if (!db.isValid()) {
0079         qCWarning(RUQOLA_DATABASE_LOG) << "The assumption was wrong, deleteMessage was called before addMessage, in account" << accountName << "room"
0080                                        << roomName << "database file " << dbName;
0081         return false;
0082     }
0083     Q_ASSERT(db.isOpen());
0084     return true;
0085 }
0086 
0087 bool LocalDatabaseBase::checkDataBase(const QString &accountName, QSqlDatabase &db)
0088 {
0089     const QString dbName = databaseName(accountName);
0090     db = QSqlDatabase::database(dbName);
0091     if (!db.isValid()) {
0092         qCWarning(RUQOLA_DATABASE_LOG) << "The assumption was wrong, deleteMessage was called before addMessage, in account" << accountName << "database file "
0093                                        << dbName;
0094         return false;
0095     }
0096     Q_ASSERT(db.isValid());
0097     Q_ASSERT(db.isOpen());
0098     return true;
0099 }
0100 
0101 bool LocalDatabaseBase::initializeDataBase(const QString &accountName, const QString &_roomName, QSqlDatabase &db)
0102 {
0103     const QString roomName = LocalDatabaseUtils::fixRoomName(_roomName);
0104     const QString dbName = databaseName(accountName + QLatin1Char('-') + roomName);
0105     db = QSqlDatabase::database(dbName);
0106     if (!db.isValid()) {
0107         db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), dbName);
0108         const QString dirPath = mBasePath + accountName;
0109         if (!QDir().mkpath(dirPath)) {
0110             qCWarning(RUQOLA_DATABASE_LOG) << "Couldn't create" << dirPath;
0111             return false;
0112         }
0113         const QString fileName = dbFileName(accountName, roomName);
0114         const bool newDb = QFileInfo::exists(fileName);
0115         db.setDatabaseName(fileName);
0116         if (!db.open()) {
0117             qCWarning(RUQOLA_DATABASE_LOG) << "Couldn't create" << db.databaseName();
0118             return false;
0119         }
0120         if (!newDb) {
0121             db.exec(schemaDataBase());
0122             if (db.lastError().isValid()) {
0123                 qCWarning(RUQOLA_DATABASE_LOG) << "Couldn't create table LOGS in" << db.databaseName() << ":" << db.lastError();
0124                 return false;
0125             }
0126         }
0127         // Using the write-ahead log and sync = NORMAL for faster writes
0128         // (idea taken from kactivities-stat)
0129         db.exec(QStringLiteral("PRAGMA synchronous = 1"));
0130         // use the write-ahead log (requires sqlite > 3.7.0)
0131         db.exec(QStringLiteral("PRAGMA journal_mode = WAL"));
0132     }
0133 
0134     Q_ASSERT(db.isValid());
0135     Q_ASSERT(db.isOpen());
0136     return true;
0137 }
0138 
0139 bool LocalDatabaseBase::initializeDataBase(const QString &accountName, QSqlDatabase &db)
0140 {
0141     const QString dbName = databaseName(accountName);
0142     db = QSqlDatabase::database(dbName);
0143     if (!db.isValid()) {
0144         db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), dbName);
0145         const QString dirPath = mBasePath + accountName;
0146         if (!QDir().mkpath(dirPath)) {
0147             qCWarning(RUQOLA_DATABASE_LOG) << "Couldn't create" << dirPath;
0148             return false;
0149         }
0150         const QString fileName = dbFileName(accountName);
0151         const bool newDb = QFileInfo::exists(fileName);
0152         db.setDatabaseName(fileName);
0153         if (!db.open()) {
0154             qCWarning(RUQOLA_DATABASE_LOG) << "Couldn't create" << db.databaseName();
0155             return false;
0156         }
0157         if (!newDb) {
0158             db.exec(schemaDataBase());
0159             if (db.lastError().isValid()) {
0160                 qCWarning(RUQOLA_DATABASE_LOG) << "Couldn't create table LOGS in" << db.databaseName() << ":" << db.lastError();
0161                 return false;
0162             }
0163         }
0164         // Using the write-ahead log and sync = NORMAL for faster writes
0165         // (idea taken from kactivities-stat)
0166         db.exec(QStringLiteral("PRAGMA synchronous = 1"));
0167         // use the write-ahead log (requires sqlite > 3.7.0)
0168         db.exec(QStringLiteral("PRAGMA journal_mode = WAL"));
0169     }
0170 
0171     Q_ASSERT(db.isValid());
0172     Q_ASSERT(db.isOpen());
0173     return true;
0174 }