diff --git a/resources/sql/db_init_sqlite.sql b/resources/sql/db_init_sqlite.sql index 13ccdad12..80b5f7a53 100644 --- a/resources/sql/db_init_sqlite.sql +++ b/resources/sql/db_init_sqlite.sql @@ -69,6 +69,7 @@ CREATE TABLE Messages ( account_id INTEGER NOT NULL, custom_id TEXT, custom_hash TEXT, + Labels TEXT NOT NULL DEFAULT "." /* Holds list of assigned labels. */ FOREIGN KEY (account_id) REFERENCES Accounts (id) ON DELETE CASCADE ); diff --git a/resources/sql/db_update_mysql_4_5.sql b/resources/sql/db_update_mysql_4_5.sql new file mode 100755 index 000000000..b9f20664b --- /dev/null +++ b/resources/sql/db_update_mysql_4_5.sql @@ -0,0 +1,8 @@ +USE ##; +-- ! +SET FOREIGN_KEY_CHECKS = 0; +-- ! +!! db_update_sqlite_4_5.sql +-- ! +SET FOREIGN_KEY_CHECKS = 1; +-- ! \ No newline at end of file diff --git a/resources/sql/db_update_sqlite_4_5.sql b/resources/sql/db_update_sqlite_4_5.sql new file mode 100755 index 000000000..998f843ce --- /dev/null +++ b/resources/sql/db_update_sqlite_4_5.sql @@ -0,0 +1,4 @@ +/* Copy label IDs to Messages table. */ + + +/* Remove LabelsInMessages table. */ \ No newline at end of file diff --git a/src/librssguard/core/message.cpp b/src/librssguard/core/message.cpp index 5ec018f25..175833b20 100644 --- a/src/librssguard/core/message.cpp +++ b/src/librssguard/core/message.cpp @@ -117,7 +117,7 @@ void Message::sanitize(const Feed* feed, bool fix_future_datetimes) { } Message Message::fromSqlRecord(const QSqlRecord& record, bool* result) { - if (record.count() != MSG_DB_LABELS + 1) { + if (record.count() != MSG_DB_LABELS_IDS + 1) { if (result != nullptr) { *result = false; } diff --git a/src/librssguard/core/messagesmodel.cpp b/src/librssguard/core/messagesmodel.cpp index 06fe233fb..6da8ffd82 100644 --- a/src/librssguard/core/messagesmodel.cpp +++ b/src/librssguard/core/messagesmodel.cpp @@ -247,7 +247,8 @@ void MessagesModel::setupHeaderData() { /*: Tooltip for custom hash string of message.*/ tr("Custom hash") << /*: Tooltip for name of feed for message.*/ tr("Feed") << /*: Tooltip for indication of presence of enclosures.*/ tr("Has enclosures") << - /*: Tooltip for indication of labels of message.*/ tr("Assigned labels"); + /*: Tooltip for indication of labels of message.*/ tr("Assigned labels") << + /*: Tooltip for indication of label IDs of message.*/ tr("Assigned label IDs"); m_tooltipData << tr("ID of the article.") << tr("Is article read?") << tr("Is article important?") << tr("Is article deleted?") << tr("Is article permanently deleted from recycle bin?") @@ -256,7 +257,8 @@ void MessagesModel::setupHeaderData() { << tr("Contents of the article.") << tr("List of attachments.") << tr("Score of the article.") << tr("Account ID of the article.") << tr("Custom ID of the article.") << tr("Custom hash of the article.") << tr("Name of feed of the article.") - << tr("Indication of enclosures presence within the article.") << tr("Labels assigned to the article."); + << tr("Indication of enclosures presence within the article.") << tr("Labels assigned to the article.") + << tr("Label IDs assigned to the article."); } Qt::ItemFlags MessagesModel::flags(const QModelIndex& index) const { diff --git a/src/librssguard/core/messagesmodelsqllayer.cpp b/src/librssguard/core/messagesmodelsqllayer.cpp index e4b615532..0eb58236a 100644 --- a/src/librssguard/core/messagesmodelsqllayer.cpp +++ b/src/librssguard/core/messagesmodelsqllayer.cpp @@ -34,6 +34,7 @@ MessagesModelSqlLayer::MessagesModelSqlLayer() m_orderByNames[MSG_DB_FEED_TITLE_INDEX] = QSL("Feeds.title"); m_orderByNames[MSG_DB_HAS_ENCLOSURES] = QSL("has_enclosures"); m_orderByNames[MSG_DB_LABELS] = QSL("msg_labels"); + m_orderByNames[MSG_DB_LABELS_IDS] = QSL("Messages.labels"); m_numericColumns << MSG_DB_ID_INDEX << MSG_DB_READ_INDEX << MSG_DB_DELETED_INDEX << MSG_DB_PDELETED_INDEX << MSG_DB_IMPORTANT_INDEX << MSG_DB_ACCOUNT_ID_INDEX << MSG_DB_DCREATED_INDEX << MSG_DB_SCORE_INDEX; diff --git a/src/librssguard/database/databasecleaner.cpp b/src/librssguard/database/databasecleaner.cpp index db32291ae..d4b3dec9f 100644 --- a/src/librssguard/database/databasecleaner.cpp +++ b/src/librssguard/database/databasecleaner.cpp @@ -60,8 +60,6 @@ void DatabaseCleaner::purgeDatabaseData(CleanerOrders which_data) { emit purgeProgress(progress, tr("Starred articles purged...")); } - result &= DatabaseQueries::purgeLeftoverLabelAssignments(database); - if (which_data.m_shrinkDatabase) { progress += difference; emit purgeProgress(progress, tr("Shrinking database file...")); diff --git a/src/librssguard/database/databasequeries.cpp b/src/librssguard/database/databasequeries.cpp index 94aac33b2..4e061d5c5 100644 --- a/src/librssguard/database/databasequeries.cpp +++ b/src/librssguard/database/databasequeries.cpp @@ -37,10 +37,17 @@ QMap DatabaseQueries::messageTableAttributes(bool only_msg_table) "THEN 'true' " "ELSE 'false' " "END AS has_enclosures"); + + field_names[MSG_DB_LABELS] = QSL("1 as msg_labels"); + field_names[MSG_DB_LABELS_IDS] = QSL("Messages.labels"); + + // TODO: zpomaluje zobrazení seznamu zpráv + /* field_names[MSG_DB_LABELS] = QSL("(SELECT GROUP_CONCAT(Labels.name) FROM Labels WHERE Labels.custom_id IN (SELECT " "LabelsInMessages.label FROM LabelsInMessages WHERE LabelsInMessages.account_id = " "Messages.account_id AND LabelsInMessages.message = Messages.custom_id)) as msg_labels"); + */ return field_names; } @@ -68,10 +75,14 @@ QVariantHash DatabaseQueries::deserializeCustomData(const QString& data) { bool DatabaseQueries::isLabelAssignedToMessage(const QSqlDatabase& db, Label* label, const Message& msg) { QSqlQuery q(db); + // TODO: ověřit q.setForwardOnly(true); - q.prepare(QSL("SELECT COUNT(*) FROM LabelsInMessages " - "WHERE label = :label AND message = :message AND account_id = :account_id;")); - q.bindValue(QSL(":label"), label->customId()); + q.prepare(QSL("SELECT COUNT(*) FROM Messages " + "WHERE " + "Messages.labels LIKE :label AND " + "Messages.custom_id = :message AND " + "account_id = :account_id;")); + q.bindValue(QSL(":label"), QSL("%.%1.%").arg(label->customId())); q.bindValue(QSL(":message"), msg.m_customId); q.bindValue(QSL(":account_id"), label->getParentServiceRoot()->accountId()); @@ -83,10 +94,12 @@ bool DatabaseQueries::isLabelAssignedToMessage(const QSqlDatabase& db, Label* la bool DatabaseQueries::deassignLabelFromMessage(const QSqlDatabase& db, Label* label, const Message& msg) { QSqlQuery q(db); + // TODO: ověřit. q.setForwardOnly(true); - q.prepare(QSL("DELETE FROM LabelsInMessages " - "WHERE label = :label AND message = :message AND account_id = :account_id;")); - q.bindValue(QSL(":label"), label->customId()); + q.prepare(QSL("UPDATE Messages " + "SET Messages.labels = REPLACE(Messages.labels, :label, \".\") " + "WHERE Messages.custom_id = :message AND account_id = :account_id;")); + q.bindValue(QSL(":label"), QSL(".%1.").arg(label->customId())); q.bindValue(QSL(":message"), msg.m_customId.isEmpty() ? QString::number(msg.m_id) : msg.m_customId); q.bindValue(QSL(":account_id"), label->getParentServiceRoot()->accountId()); @@ -94,57 +107,44 @@ bool DatabaseQueries::deassignLabelFromMessage(const QSqlDatabase& db, Label* la } bool DatabaseQueries::assignLabelToMessage(const QSqlDatabase& db, Label* label, const Message& msg) { + deassignLabelFromMessage(db, label, msg); + QSqlQuery q(db); + // TODO: ověřit. q.setForwardOnly(true); - q.prepare(QSL("DELETE FROM LabelsInMessages " - "WHERE label = :label AND message = :message AND account_id = :account_id;")); - q.bindValue(QSL(":label"), label->customId()); + q.prepare(QSL("UPDATE Messages " + "SET Messages.labels = Messages.labels || :label " + "WHERE Messages.custom_id = :message AND account_id = :account_id;")); + q.bindValue(QSL(":label"), QSL("%1.").arg(label->customId())); q.bindValue(QSL(":message"), msg.m_customId.isEmpty() ? QString::number(msg.m_id) : msg.m_customId); q.bindValue(QSL(":account_id"), label->getParentServiceRoot()->accountId()); - auto succ = q.exec(); - - if (succ) { - q.prepare(QSL("INSERT INTO LabelsInMessages (label, message, account_id) " - "VALUES (:label, :message, :account_id);")); - q.bindValue(QSL(":label"), label->customId()); - q.bindValue(QSL(":message"), msg.m_customId.isEmpty() ? QString::number(msg.m_id) : msg.m_customId); - q.bindValue(QSL(":account_id"), label->getParentServiceRoot()->accountId()); - - succ = q.exec(); - } - - return succ; + return q.exec(); } bool DatabaseQueries::setLabelsForMessage(const QSqlDatabase& db, const QList& labels, const Message& msg) { QSqlQuery q(db); + auto std_lbls = boolinq::from(labels) + .select([](Label* lbl) { + return lbl->customId(); + }) + .toStdList(); + + QStringList lbls = FROM_STD_LIST(QStringList, std_lbls); + QString lblss = QSL(".") + lbls.join('.') + QSL("."); + + // TODO: ověřit. q.setForwardOnly(true); - q.prepare(QSL("DELETE FROM LabelsInMessages WHERE message = :message AND account_id = :account_id")); - q.bindValue(QSL(":account_id"), msg.m_accountId); + q.prepare(QSL("UPDATE Messages " + "SET Messages.labels = :labels " + "WHERE Messages.custom_id = :message AND account_id = :account_id;")); + q.bindValue(QSL(":labels"), lblss); q.bindValue(QSL(":message"), msg.m_customId.isEmpty() ? QString::number(msg.m_id) : msg.m_customId); + q.bindValue(QSL(":account_id"), msg.m_accountId); - auto succ = q.exec(); - - if (!succ) { - return false; - } - - q.prepare(QSL("INSERT INTO LabelsInMessages (message, label, account_id) VALUES (:message, :label, :account_id);")); - - for (const Label* label : labels) { - q.bindValue(QSL(":account_id"), msg.m_accountId); - q.bindValue(QSL(":message"), msg.m_customId.isEmpty() ? QString::number(msg.m_id) : msg.m_customId); - q.bindValue(QSL(":label"), label->customId()); - - if (!q.exec()) { - return false; - } - } - - return true; + return q.exec(); } QList DatabaseQueries::getLabelsForAccount(const QSqlDatabase& db, int account_id) { @@ -171,21 +171,28 @@ QList DatabaseQueries::getLabelsForAccount(const QSqlDatabase& db, int a QList DatabaseQueries::getLabelsForMessage(const QSqlDatabase& db, const Message& msg, - const QList installed_labels) { + const QList& installed_labels) { QList labels; QSqlQuery q(db); + // TODO: ověřit q.setForwardOnly(true); - q.prepare(QSL("SELECT DISTINCT label FROM LabelsInMessages WHERE message = :message AND account_id = :account_id;")); + q.prepare(QSL("SELECT labels FROM Messages WHERE custom_id = :message AND account_id = :account_id;")); q.bindValue(QSL(":account_id"), msg.m_accountId); q.bindValue(QSL(":message"), msg.m_customId.isEmpty() ? QString::number(msg.m_id) : msg.m_customId); - if (q.exec()) { + if (q.exec() && q.next()) { + auto label_ids = q.value(0).toString().split('.', +#if QT_VERSION >= 0x050F00 // Qt >= 5.15.0 + Qt::SplitBehaviorFlags::SkipEmptyParts); +#else + QString::SplitBehavior::SkipEmptyParts); +#endif + auto iter = boolinq::from(installed_labels); - while (q.next()) { - auto lbl_id = q.value(0).toString(); + for (const QString& lbl_id : label_ids) { Label* candidate_label = iter.firstOrDefault([&](const Label* lbl) { return lbl->customId() == lbl_id; }); @@ -223,9 +230,12 @@ bool DatabaseQueries::deleteLabel(const QSqlDatabase& db, Label* label) { q.bindValue(QSL(":id"), label->id()); q.bindValue(QSL(":account_id"), label->getParentServiceRoot()->accountId()); + // TODO: ověřit if (q.exec()) { - q.prepare(QSL("DELETE FROM LabelsInMessages WHERE label = :custom_id AND account_id = :account_id;")); - q.bindValue(QSL(":custom_id"), label->customId()); + q.prepare(QSL("UPDATE Messages " + "SET Messages.labels = REPLACE(Messages.labels, :label, \".\") " + "WHERE account_id = :account_id;")); + q.bindValue(QSL(":label"), QSL(".%1.").arg(label->customId())); q.bindValue(QSL(":account_id"), label->getParentServiceRoot()->accountId()); return q.exec(); @@ -267,18 +277,17 @@ bool DatabaseQueries::createLabel(const QSqlDatabase& db, Label* label, int acco bool DatabaseQueries::markLabelledMessagesReadUnread(const QSqlDatabase& db, Label* label, RootItem::ReadStatus read) { QSqlQuery q(db); + // TODO: ověřit q.setForwardOnly(true); q.prepare(QSL("UPDATE Messages SET is_read = :read " "WHERE " " is_deleted = 0 AND " " is_pdeleted = 0 AND " " account_id = :account_id AND " - " EXISTS (SELECT * FROM LabelsInMessages WHERE LabelsInMessages.label = :label AND " - "Messages.account_id = LabelsInMessages.account_id AND Messages.custom_id = " - "LabelsInMessages.message);")); + " labels LIKE :label;")); q.bindValue(QSL(":read"), read == RootItem::ReadStatus::Read ? 1 : 0); q.bindValue(QSL(":account_id"), label->getParentServiceRoot()->accountId()); - q.bindValue(QSL(":label"), label->customId()); + q.bindValue(QSL(":label"), QSL("%.%1.%").arg(label->customId())); return q.exec(); } @@ -619,27 +628,27 @@ int DatabaseQueries::getMessageCountsForLabel(const QSqlDatabase& db, q.setForwardOnly(true); + // TODO: ověřit if (only_total_counts) { q.prepare(QSL("SELECT COUNT(*) FROM Messages " - "INNER JOIN LabelsInMessages " - "ON " - " Messages.is_pdeleted = 0 AND Messages.is_deleted = 0 AND " - " LabelsInMessages.account_id = :account_id AND LabelsInMessages.account_id = Messages.account_id " - "AND " - " LabelsInMessages.label = :label AND LabelsInMessages.message = Messages.custom_id;")); + "WHERE " + " is_deleted = 0 AND " + " is_pdeleted = 0 AND " + " account_id = :account_id AND " + " labels LIKE :label;")); } else { q.prepare(QSL("SELECT COUNT(*) FROM Messages " - "INNER JOIN LabelsInMessages " - "ON " - " Messages.is_pdeleted = 0 AND Messages.is_deleted = 0 AND Messages.is_read = 0 AND " - " LabelsInMessages.account_id = :account_id AND LabelsInMessages.account_id = Messages.account_id " - "AND " - " LabelsInMessages.label = :label AND LabelsInMessages.message = Messages.custom_id;")); + "WHERE " + " is_deleted = 0 AND " + " is_pdeleted = 0 AND " + " is_read = 0 AND " + " account_id = :account_id AND " + " labels LIKE :label;")); } q.bindValue(QSL(":account_id"), account_id); - q.bindValue(QSL(":label"), label->customId()); + q.bindValue(QSL(":label"), QSL("%.%1.%").arg(label->customId())); if (q.exec() && q.next()) { if (ok != nullptr) { @@ -757,20 +766,20 @@ QList DatabaseQueries::getUndeletedMessagesWithLabel(const QSqlDatabase QList messages; QSqlQuery q(db); + // TODO: ověřit q.prepare(QSL("SELECT %1 " "FROM Messages " "INNER JOIN Feeds " "ON Messages.feed = Feeds.custom_id AND Messages.account_id = :account_id AND Messages.account_id = " "Feeds.account_id " - "INNER JOIN LabelsInMessages " - "ON " - " Messages.is_pdeleted = 0 AND Messages.is_deleted = 0 AND " - " LabelsInMessages.account_id = :account_id AND LabelsInMessages.account_id = Messages.account_id AND " - " LabelsInMessages.label = :label AND " - " LabelsInMessages.message = Messages.custom_id;") + "WHERE " + " is_deleted = 0 AND " + " is_pdeleted = 0 AND " + " account_id = :account_id AND " + " labels LIKE :label;") .arg(messageTableAttributes(true).values().join(QSL(", ")))); q.bindValue(QSL(":account_id"), label->getParentServiceRoot()->accountId()); - q.bindValue(QSL(":label"), label->customId()); + q.bindValue(QSL(":label"), QSL("%.%1.%").arg(label->customId())); if (q.exec()) { while (q.next()) { @@ -799,14 +808,16 @@ QList DatabaseQueries::getUndeletedLabelledMessages(const QSqlDatabase& QList messages; QSqlQuery q(db); + // TODO: ověřit q.prepare(QSL("SELECT %1 " "FROM Messages " "LEFT JOIN Feeds " "ON Messages.feed = Feeds.custom_id AND Messages.account_id = Feeds.account_id " - "WHERE Messages.is_deleted = 0 AND Messages.is_pdeleted = 0 AND Messages.account_id = :account_id AND " - " (SELECT COUNT(*) FROM LabelsInMessages " - " WHERE account_id = :account_id AND " - " message = Messages.custom_id) > 0;") + "WHERE " + " is_deleted = 0 AND " + " is_pdeleted = 0 AND " + " account_id = :account_id AND " + " LENGTH(labels) > 2;") .arg(messageTableAttributes(true).values().join(QSL(", ")))); q.bindValue(QSL(":account_id"), account_id); @@ -1054,12 +1065,14 @@ QHash DatabaseQueries::bagsOfMessages(const QSqlDatabase& q.setForwardOnly(true); - q.prepare(QSL("SELECT message " - "FROM LabelsInMessages " - "WHERE label = :label AND account_id = :account_id;")); + // TODO: ověřit + q.prepare(QSL("SELECT custom_id FROM Messages " + "WHERE " + " account_id = :account_id AND " + " labels LIKE :label;")); for (const Label* lbl : labels) { - q.bindValue(QSL(":label"), lbl->customId()); + q.bindValue(QSL(":label"), QSL("%.%1.%").arg(lbl->customId())); q.bindValue(QSL(":account_id"), lbl->getParentServiceRoot()->accountId()); q.exec(); @@ -1521,7 +1534,6 @@ bool DatabaseQueries::deleteAccount(const QSqlDatabase& db, ServiceRoot* account QStringList queries; queries << QSL("DELETE FROM MessageFiltersInFeeds WHERE account_id = :account_id;") - << QSL("DELETE FROM LabelsInMessages WHERE account_id = :account_id;") << QSL("DELETE FROM Messages WHERE account_id = :account_id;") << QSL("DELETE FROM Feeds WHERE account_id = :account_id;") << QSL("DELETE FROM Categories WHERE account_id = :account_id;") @@ -1568,12 +1580,6 @@ bool DatabaseQueries::deleteAccountData(const QSqlDatabase& db, q.bindValue(QSL(":account_id"), account_id); result &= q.exec(); - if (delete_messages_too) { - q.prepare(QSL("DELETE FROM LabelsInMessages WHERE account_id = :account_id;")); - q.bindValue(QSL(":account_id"), account_id); - result &= q.exec(); - } - if (delete_labels_too) { q.prepare(QSL("DELETE FROM Labels WHERE account_id = :account_id;")); q.bindValue(QSL(":account_id"), account_id); @@ -1591,31 +1597,27 @@ bool DatabaseQueries::cleanLabelledMessages(const QSqlDatabase& db, bool clean_r if (clean_read_only) { q.prepare(QSL("UPDATE Messages SET is_deleted = :deleted " "WHERE " - " is_deleted = 0 AND " - " is_pdeleted = 0 AND " - " is_read = 1 AND " - " account_id = :account_id AND " - " EXISTS (SELECT * FROM LabelsInMessages WHERE LabelsInMessages.label = :label AND " - "Messages.account_id = LabelsInMessages.account_id AND Messages.custom_id = " - "LabelsInMessages.message);")); + " is_deleted = 0 AND " + " is_pdeleted = 0 AND " + " is_read = 1 AND " + " account_id = :account_id AND " + " labels LIKE :label;")); } else { q.prepare(QSL("UPDATE Messages SET is_deleted = :deleted " "WHERE " - " is_deleted = 0 AND " - " is_pdeleted = 0 AND " - " account_id = :account_id AND " - " EXISTS (SELECT * FROM LabelsInMessages WHERE LabelsInMessages.label = :label AND " - "Messages.account_id = LabelsInMessages.account_id AND Messages.custom_id = " - "LabelsInMessages.message);")); + " is_deleted = 0 AND " + " is_pdeleted = 0 AND " + " account_id = :account_id AND " + " labels LIKE :label;")); } q.bindValue(QSL(":deleted"), 1); q.bindValue(QSL(":account_id"), label->getParentServiceRoot()->accountId()); - q.bindValue(QSL(":label"), label->customId()); + q.bindValue(QSL(":label"), QSL("%.%1.%").arg(label->customId())); if (!q.exec()) { - qWarningNN << LOGSEC_DB << "Cleaning of labelled messages failed: '" << q.lastError().text() << "'."; + qWarningNN << LOGSEC_DB << "Cleaning of labelled messages failed:" << QUOTE_W_SPACE_DOT(q.lastError().text()); return false; } else { @@ -1735,48 +1737,6 @@ bool DatabaseQueries::purgeLeftoverMessages(const QSqlDatabase& db, int account_ } } -bool DatabaseQueries::purgeLeftoverLabelAssignments(const QSqlDatabase& db, int account_id) { - QSqlQuery q(db); - bool succ = false; - - if (account_id <= 0) { - succ = q.exec(QSL("DELETE FROM LabelsInMessages " - "WHERE NOT EXISTS (SELECT * FROM Messages WHERE Messages.account_id = " - "LabelsInMessages.account_id AND Messages.custom_id = LabelsInMessages.message);")) && - q.exec(QSL("DELETE FROM LabelsInMessages " - "WHERE NOT EXISTS (SELECT * FROM Labels WHERE Labels.account_id = LabelsInMessages.account_id " - "AND Labels.custom_id = LabelsInMessages.label);")); - } - else { - q.prepare(QSL("DELETE FROM LabelsInMessages " - "WHERE account_id = :account_id AND " - " (message NOT IN (SELECT custom_id FROM Messages WHERE account_id = :account_id) OR " - " label NOT IN (SELECT custom_id FROM Labels WHERE account_id = :account_id));")); - q.bindValue(QSL(":account_id"), account_id); - succ = q.exec(); - } - - if (!succ) { - qWarningNN << LOGSEC_DB << "Removing of leftover label assignments failed: '" << q.lastError().text() << "'."; - } - - return succ; -} - -bool DatabaseQueries::purgeLabelsAndLabelAssignments(const QSqlDatabase& db, int account_id) { - QSqlQuery q(db); - - q.prepare(QSL("DELETE FROM LabelsInMessages WHERE account_id = :account_id;")); - q.bindValue(QSL(":account_id"), account_id); - auto succ = q.exec(); - - q.prepare(QSL("DELETE FROM Labels WHERE account_id = :account_id;")); - q.bindValue(QSL(":account_id"), account_id); - succ &= q.exec(); - - return succ; -} - void DatabaseQueries::storeAccountTree(const QSqlDatabase& db, RootItem* tree_root, int account_id) { // Iterate all children. auto str = tree_root->getSubTree(); @@ -1835,6 +1795,7 @@ QStringList DatabaseQueries::customIdsOfMessagesFromLabel(const QSqlDatabase& db QSqlQuery q(db); QStringList ids; + // TODO: ověřit q.setForwardOnly(true); q.prepare(QSL("SELECT custom_id FROM Messages " "WHERE " @@ -1842,11 +1803,9 @@ QStringList DatabaseQueries::customIdsOfMessagesFromLabel(const QSqlDatabase& db " is_deleted = 0 AND " " is_pdeleted = 0 AND " " account_id = :account_id AND " - " EXISTS (SELECT * FROM LabelsInMessages WHERE LabelsInMessages.label = :label AND " - "Messages.account_id = LabelsInMessages.account_id AND Messages.custom_id = " - "LabelsInMessages.message);")); + " labels LIKE :label;")); q.bindValue(QSL(":account_id"), label->getParentServiceRoot()->accountId()); - q.bindValue(QSL(":label"), label->customId()); + q.bindValue(QSL(":label"), QSL("%.%1.%").arg(label->customId())); q.bindValue(QSL(":read"), target_read == RootItem::ReadStatus::Read ? 0 : 1); if (ok != nullptr) { @@ -2202,8 +2161,7 @@ bool DatabaseQueries::deleteFeed(const QSqlDatabase& db, Feed* feed, int account q.bindValue(QSL(":feed"), feed->customId()); q.bindValue(QSL(":account_id"), account_id); - return q.exec() && purgeLeftoverMessageFilterAssignments(db, account_id) && - purgeLeftoverLabelAssignments(db, account_id); + return q.exec() && purgeLeftoverMessageFilterAssignments(db, account_id); } bool DatabaseQueries::deleteCategory(const QSqlDatabase& db, Category* category) { diff --git a/src/librssguard/database/databasequeries.h b/src/librssguard/database/databasequeries.h index ddfbc54df..8dd2aea80 100644 --- a/src/librssguard/database/databasequeries.h +++ b/src/librssguard/database/databasequeries.h @@ -36,7 +36,7 @@ class DatabaseQueries { static QList getLabelsForAccount(const QSqlDatabase& db, int account_id); static QList getLabelsForMessage(const QSqlDatabase& db, const Message& msg, - const QList installed_labels); + const QList& installed_labels); static bool updateLabel(const QSqlDatabase& db, Label* label); static bool deleteLabel(const QSqlDatabase& db, Label* label); static bool createLabel(const QSqlDatabase& db, Label* label, int account_id); @@ -67,11 +67,6 @@ class DatabaseQueries { static bool purgeMessagesFromBin(const QSqlDatabase& db, bool clear_only_read, int account_id); static bool purgeLeftoverMessages(const QSqlDatabase& db, int account_id); - // Purges message/label assignments where source message or label does not exist. - // If account ID smaller than 1 is passed, then do this for all accounts. - static bool purgeLeftoverLabelAssignments(const QSqlDatabase& db, int account_id = 0); - static bool purgeLabelsAndLabelAssignments(const QSqlDatabase& db, int account_id); - // Counts of unread/all messages. static QMap> getMessageCountsForCategory(const QSqlDatabase& db, const QString& custom_id, @@ -125,9 +120,7 @@ class DatabaseQueries { RootItem::ReadStatus target_read, int account_id, bool* ok = nullptr); - static QStringList customIdsOfUnreadMessages(const QSqlDatabase& db, - int account_id, - bool* ok = nullptr); + static QStringList customIdsOfUnreadMessages(const QSqlDatabase& db, int account_id, bool* ok = nullptr); static QStringList customIdsOfMessagesFromAccount(const QSqlDatabase& db, RootItem::ReadStatus target_read, int account_id, diff --git a/src/librssguard/definitions/definitions.h b/src/librssguard/definitions/definitions.h index 79e087823..214fbce4a 100644 --- a/src/librssguard/definitions/definitions.h +++ b/src/librssguard/definitions/definitions.h @@ -268,6 +268,7 @@ #define MSG_DB_FEED_TITLE_INDEX 16 #define MSG_DB_HAS_ENCLOSURES 17 #define MSG_DB_LABELS 18 +#define MSG_DB_LABELS_IDS 19 // Indexes of columns as they are DEFINED IN THE TABLE for CATEGORIES. #define CAT_DB_ID_INDEX 0 diff --git a/src/librssguard/gui/dialogs/formmessagefiltersmanager.cpp b/src/librssguard/gui/dialogs/formmessagefiltersmanager.cpp index c3ec1b280..3d83f4f8b 100644 --- a/src/librssguard/gui/dialogs/formmessagefiltersmanager.cpp +++ b/src/librssguard/gui/dialogs/formmessagefiltersmanager.cpp @@ -392,7 +392,6 @@ void FormMessageFiltersManager::processCheckedFeeds() { // Purge the message completely and remove leftovers. DatabaseQueries::purgeMessage(database, msg->m_id); - DatabaseQueries::purgeLeftoverLabelAssignments(database, msg->m_accountId); } else if (result == MessageObject::FilteringAction::Ignore) { remove_from_list = true; diff --git a/src/librssguard/gui/messagesview.cpp b/src/librssguard/gui/messagesview.cpp index eac23f1f8..a54b88a30 100644 --- a/src/librssguard/gui/messagesview.cpp +++ b/src/librssguard/gui/messagesview.cpp @@ -851,6 +851,10 @@ void MessagesView::adjustColumns() { hideColumn(MSG_DB_FEED_TITLE_INDEX); hideColumn(MSG_DB_HAS_ENCLOSURES); hideColumn(MSG_DB_LABELS); + +#if !defined(NDEBUG) + hideColumn(MSG_DB_LABELS_IDS); +#endif } } diff --git a/src/librssguard/services/abstract/serviceroot.cpp b/src/librssguard/services/abstract/serviceroot.cpp index 988534f51..6ad1899c1 100644 --- a/src/librssguard/services/abstract/serviceroot.cpp +++ b/src/librssguard/services/abstract/serviceroot.cpp @@ -270,12 +270,6 @@ void ServiceRoot::removeLeftOverMessageFilterAssignments() { DatabaseQueries::purgeLeftoverMessageFilterAssignments(database, accountId()); } -void ServiceRoot::removeLeftOverMessageLabelAssignments() { - QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className()); - - DatabaseQueries::purgeLeftoverLabelAssignments(database, accountId()); -} - QList ServiceRoot::undeletedMessages() const { QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className()); @@ -506,7 +500,6 @@ void ServiceRoot::syncIn() { // so remove left over messages and filter assignments. removeLeftOverMessages(); removeLeftOverMessageFilterAssignments(); - removeLeftOverMessageLabelAssignments(); auto chi = new_tree->childItems(); @@ -724,16 +717,14 @@ bool ServiceRoot::loadMessagesForItem(RootItem* item, MessagesModel* model) { } else if (item->kind() == RootItem::Kind::Label) { // Show messages with particular label. - model->setFilter(QSL("Messages.is_deleted = 0 AND Messages.is_pdeleted = 0 AND Messages.account_id = %1 AND " - "(SELECT COUNT(*) FROM LabelsInMessages WHERE account_id = %1 AND message = " - "Messages.custom_id AND label = '%2') > 0") + model->setFilter(QSL("Messages.is_deleted = 0 AND Messages.is_pdeleted = 0 AND " + "Messages.labels LIKE \"%.%2.%\" AND Messages.account_id = %1") .arg(QString::number(accountId()), item->customId())); } else if (item->kind() == RootItem::Kind::Labels) { // Show messages with any label. - model->setFilter(QSL("Messages.is_deleted = 0 AND Messages.is_pdeleted = 0 AND Messages.account_id = %1 AND " - "(SELECT COUNT(*) FROM LabelsInMessages WHERE account_id = %1 AND message = " - "Messages.custom_id) > 0") + model->setFilter(QSL("Messages.is_deleted = 0 AND Messages.is_pdeleted = 0 AND " + "LENGTH(Messages.labels) > 2 AND Messages.account_id = %1") .arg(QString::number(accountId()))); } else if (item->kind() == RootItem::Kind::ServiceRoot) { diff --git a/src/librssguard/services/abstract/serviceroot.h b/src/librssguard/services/abstract/serviceroot.h index edb391c0a..bd8686f53 100644 --- a/src/librssguard/services/abstract/serviceroot.h +++ b/src/librssguard/services/abstract/serviceroot.h @@ -264,11 +264,6 @@ class ServiceRoot : public RootItem { // from another machine and then performs sync-in on this machine. void removeLeftOverMessageFilterAssignments(); - // Removes all labels/message assignments which are - // assigned to non-existing messages or which are - // assigned from non-existing labels. - void removeLeftOverMessageLabelAssignments(); - // Takes lists of feeds/categories and assembles them into the tree structure. void assembleCategories(const Assignment& categories); void assembleFeeds(const Assignment& feeds);