File indexing completed on 2024-05-12 05:02:00

0001 /*
0002    SPDX-FileCopyrightText: 2023-2024 Laurent Montel <montel.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "localmessagedatabase.h"
0008 #include "localdatabaseutils.h"
0009 #include "messages/message.h"
0010 #include "rocketchataccount.h"
0011 #include "ruqola_database_debug.h"
0012 
0013 #include <QDir>
0014 #include <QJsonDocument>
0015 #include <QSqlDatabase>
0016 #include <QSqlError>
0017 #include <QSqlQuery>
0018 #include <QSqlRecord>
0019 #include <QSqlTableModel>
0020 
0021 static const char s_schemaMessageDataBase[] = "CREATE TABLE MESSAGES (messageId TEXT PRIMARY KEY NOT NULL, timestamp INTEGER, json TEXT)";
0022 enum class MessagesFields {
0023     MessageId,
0024     TimeStamp,
0025     Json,
0026 }; // in the same order as the table
0027 
0028 LocalMessageDatabase::LocalMessageDatabase()
0029     : LocalDatabaseBase(LocalDatabaseUtils::localMessagesDatabasePath(), LocalDatabaseBase::DatabaseType::Message)
0030 {
0031 }
0032 
0033 LocalMessageDatabase::~LocalMessageDatabase() = default;
0034 
0035 QString LocalMessageDatabase::schemaDataBase() const
0036 {
0037     return QString::fromLatin1(s_schemaMessageDataBase);
0038 }
0039 
0040 void LocalMessageDatabase::addMessage(const QString &accountName, const QString &roomName, const Message &m)
0041 {
0042     QSqlDatabase db;
0043     if (initializeDataBase(accountName, roomName, db)) {
0044         QSqlQuery query(LocalDatabaseUtils::insertReplaceMessages(), db);
0045         query.addBindValue(m.messageId());
0046         query.addBindValue(m.timeStamp());
0047         // qDebug() << " m.timeStamp() " << m.timeStamp();
0048         // FIXME look at why we can't save a binary ?
0049         query.addBindValue(Message::serialize(m, false)); // TODO binary or not ?
0050         if (!query.exec()) {
0051             qCWarning(RUQOLA_DATABASE_LOG) << "Couldn't insert-or-replace in MESSAGES table" << db.databaseName() << query.lastError();
0052         }
0053     }
0054 }
0055 
0056 void LocalMessageDatabase::deleteMessage(const QString &accountName, const QString &roomName, const QString &messageId)
0057 {
0058     QSqlDatabase db;
0059     if (!checkDataBase(accountName, roomName, db)) {
0060         return;
0061     }
0062     QSqlQuery query(LocalDatabaseUtils::deleteMessage(), db);
0063     query.addBindValue(messageId);
0064     if (!query.exec()) {
0065         qCWarning(RUQOLA_DATABASE_LOG) << "Couldn't insert-or-replace in MESSAGES table" << db.databaseName() << query.lastError();
0066     }
0067 }
0068 
0069 QString LocalMessageDatabase::generateQueryStr(qint64 startId, qint64 endId, qint64 numberElements)
0070 {
0071     QString query = QStringLiteral("SELECT * FROM MESSAGES");
0072     if (startId != -1) {
0073         query += QStringLiteral(" WHERE timestamp >= :startId");
0074         if (endId != -1) {
0075             query += QStringLiteral(" AND timestamp <= :endId");
0076         }
0077     } else {
0078         if (endId != -1) {
0079             query += QStringLiteral(" WHERE timestamp <= :endId");
0080         }
0081     }
0082     query += QStringLiteral(" ORDER BY timestamp DESC");
0083 
0084     if (numberElements != -1) {
0085         query += QStringLiteral(" LIMIT :limit");
0086     }
0087     return query;
0088 }
0089 
0090 QVector<Message>
0091 LocalMessageDatabase::loadMessages(RocketChatAccount *account, const QString &_roomName, qint64 startId, qint64 endId, qint64 numberElements) const
0092 {
0093     Q_ASSERT(account);
0094     return loadMessages(account->accountName(), _roomName, startId, endId, numberElements, account->emojiManager());
0095 }
0096 
0097 QVector<Message> LocalMessageDatabase::loadMessages(const QString &accountName,
0098                                                     const QString &_roomName,
0099                                                     qint64 startId,
0100                                                     qint64 endId,
0101                                                     qint64 numberElements,
0102                                                     EmojiManager *emojiManager) const
0103 {
0104 #if 0
0105     SELECT id, nom, email
0106     FROM utilisateurs
0107     LIMIT 5 OFFSET 5;
0108 
0109     // Use sorting ASC or DESC
0110 #endif
0111 
0112     const QString roomName = LocalDatabaseUtils::fixRoomName(_roomName);
0113     const QString dbName = databaseName(accountName + QLatin1Char('-') + roomName);
0114     QSqlDatabase db = QSqlDatabase::database(dbName);
0115     if (!db.isValid()) {
0116         // Open the DB if it exists (don't create a new one)
0117         const QString fileName = dbFileName(accountName, roomName);
0118         // qDebug() << " fileName " << fileName;
0119         if (!QFileInfo::exists(fileName)) {
0120             return {};
0121         }
0122         db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), dbName);
0123         db.setDatabaseName(fileName);
0124         if (!db.open()) {
0125             qCWarning(RUQOLA_DATABASE_LOG) << "Couldn't open" << fileName;
0126             return {};
0127         }
0128     }
0129 
0130     Q_ASSERT(db.isValid());
0131     Q_ASSERT(db.isOpen());
0132     const QString query = LocalMessageDatabase::generateQueryStr(startId, endId, numberElements);
0133     QSqlQuery resultQuery(db);
0134     resultQuery.prepare(query);
0135     if (startId != -1) {
0136         resultQuery.bindValue(QStringLiteral(":startId"), startId);
0137         if (endId != -1) {
0138             resultQuery.bindValue(QStringLiteral(":endId"), endId);
0139         }
0140     } else {
0141         if (endId != -1) {
0142             resultQuery.bindValue(QStringLiteral(":endId"), endId);
0143         }
0144     }
0145     if (numberElements != -1) {
0146         resultQuery.bindValue(QStringLiteral(":limit"), numberElements);
0147     }
0148     if (!resultQuery.exec()) {
0149         qCWarning(RUQOLA_DATABASE_LOG) << " Impossible to execute query: " << resultQuery.lastError() << " query: " << query;
0150         return {};
0151     }
0152 
0153     QVector<Message> listMessages;
0154     while (resultQuery.next()) {
0155         const QString json = resultQuery.value(QStringLiteral("json")).toString();
0156         listMessages.append(convertJsonToMessage(json, emojiManager));
0157     }
0158     return listMessages;
0159 }
0160 
0161 Message LocalMessageDatabase::convertJsonToMessage(const QString &json, EmojiManager *emojiManager)
0162 {
0163     const QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8());
0164     const Message msg = Message::deserialize(doc.object(), emojiManager);
0165     return msg;
0166 }
0167 
0168 std::unique_ptr<QSqlTableModel> LocalMessageDatabase::createMessageModel(const QString &accountName, const QString &_roomName) const
0169 {
0170     const QString roomName = LocalDatabaseUtils::fixRoomName(_roomName);
0171     const QString dbName = databaseName(accountName + QLatin1Char('-') + roomName);
0172     QSqlDatabase db = QSqlDatabase::database(dbName);
0173     if (!db.isValid()) {
0174         // Open the DB if it exists (don't create a new one)
0175         const QString fileName = dbFileName(accountName, roomName);
0176         // qDebug() << " fileName " << fileName;
0177         if (!QFileInfo::exists(fileName)) {
0178             return {};
0179         }
0180         db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), dbName);
0181         db.setDatabaseName(fileName);
0182         if (!db.open()) {
0183             qCWarning(RUQOLA_DATABASE_LOG) << "Couldn't open" << fileName;
0184             return {};
0185         }
0186     }
0187 
0188     Q_ASSERT(db.isValid());
0189     Q_ASSERT(db.isOpen());
0190     auto model = std::make_unique<QSqlTableModel>(nullptr, db);
0191     model->setTable(QStringLiteral("MESSAGES"));
0192     model->setSort(int(MessagesFields::TimeStamp), Qt::AscendingOrder);
0193     model->select();
0194     return model;
0195 }