data/MailSearchSummary.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "data/MailSearchSummary.h" | ||
| 2 | |||
| 3 | #include "data/MailSearchIndex.h" | ||
| 4 | #include "data/SqliteInterruptScope.h" | ||
| 5 | #include "util/AddressListScan.h" | ||
| 6 | #include "util/MessageId.h" | ||
| 7 | #include <QDateTime> | ||
| 8 | #include <QLoggingCategory> | ||
| 9 | #include <QSqlError> | ||
| 10 | #include <QSqlQuery> | ||
| 11 | #include <algorithm> | ||
| 12 | |||
| 13 | // Same category as MailCache (defined there), see docs/LOGGING.md. | ||
| 14 | Q_DECLARE_LOGGING_CATEGORY(lcCache) | ||
| 15 | |||
| 16 | namespace { | ||
| 17 | // Labels get more room than the other facets: the menu drops IMAP-internal | ||
| 18 | // keywords ($Junk, $Forwarded, …) and then shows TopValues of the rest. | ||
| 19 | constexpr int TopLabelValues = 2 * MailSearchSummary::TopValues; | ||
| 20 | // One cell of an aggregate row; huge recipient lists stay bounded. | ||
| 21 | constexpr int MaximumAddressCharacters = 4096; | ||
| 22 | } // namespace | ||
| 23 | |||
| 24 | 514 | MailSearchSummaryAccumulator::MailSearchSummaryAccumulator(MailSearchSummary::Detail detail, | |
| 25 | 514 | const QDate &hoursDay) | |
| 26 | 514 | : m_detail(detail), m_hoursDay(hoursDay) { | |
| 27 |
4/4✓ Branch 13 → 14 taken 514 times.
✓ Branch 14 → 15 taken 9 times.
✓ Branch 14 → 19 taken 505 times.
✓ Branch 15 → 16 taken 9 times.
|
514 | if (m_hoursDay.isValid()) m_hours = QVector<qint64>(24, 0); |
| 28 | 514 | } | |
| 29 | |||
| 30 | // S106-209: a linear scan (util/AddressListScan.h). The former regex's lazy | ||
| 31 | // name part re-scanned the rest of a long '@'-free header from every start | ||
| 32 | // position — quadratic, up to ~1.5 s for one row, and the summary checks its | ||
| 33 | // budget between rows. | ||
| 34 | 28220 | QList<QPair<QString, QString>> MailSearchSummaryAccumulator::addresses(const QString &header) { | |
| 35 |
1/1✓ Branch 4 → 5 taken 28220 times.
|
28220 | return AddressListScan::parse(QStringView(header).left(MaximumAddressCharacters)); |
| 36 | } | ||
| 37 | |||
| 38 | 325768 | void MailSearchSummaryAccumulator::count(QHash<QString, Facet> &facet, const QString &value, const QString &label) { | |
| 39 |
2/2✓ Branch 3 → 4 taken 66828 times.
✓ Branch 3 → 5 taken 258940 times.
|
325768 | if (value.isEmpty()) return; |
| 40 | 258940 | auto &entry = facet[value]; | |
| 41 |
4/4✓ Branch 7 → 8 taken 2221 times.
✓ Branch 7 → 13 taken 256719 times.
✓ Branch 9 → 10 taken 319 times.
✓ Branch 9 → 11 taken 1902 times.
|
258940 | if (entry.label.isEmpty()) entry.label = label.isEmpty() ? value : label; |
| 42 | 258940 | ++entry.count; | |
| 43 | } | ||
| 44 | |||
| 45 | // D-32: a folder counts every mail with a copy in it, so the count next to | ||
| 46 | // a folder equals the results of picking that folder (the copy elsewhere | ||
| 47 | // may represent the mail in the list). | ||
| 48 | 105426 | void MailSearchSummaryAccumulator::countFolder(const MailSearchSummaryRow &row) { | |
| 49 |
2/2✓ Branch 2 → 3 taken 31419 times.
✓ Branch 2 → 4 taken 74007 times.
|
105426 | if (m_detail == MailSearchSummary::Detail::CountOnly) return; |
| 50 |
2/2✓ Branch 5 → 6 taken 74007 times.
✓ Branch 6 → 7 taken 74007 times.
|
74007 | const QString key = row.identity + QChar(0x1f) + row.folderPath; |
| 51 |
1/2✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 74007 times.
|
74007 | if (m_seenInFolder.contains(key)) return; |
| 52 |
1/1✓ Branch 11 → 12 taken 74007 times.
|
74007 | m_seenInFolder.insert(key); |
| 53 |
1/1✓ Branch 12 → 13 taken 74007 times.
|
74007 | count(m_folders, row.folderPath, row.folderPath); |
| 54 | 74007 | } | |
| 55 | |||
| 56 | // The local day and hour of a Unix time. QDateTime's local-time lookup costs | ||
| 57 | // microseconds per row; the zone's offset is taken once per UTC hour (zone | ||
| 58 | // transitions fall on whole hours in practice) and the rest is arithmetic. | ||
| 59 | 101083 | QPair<QDate, int> MailSearchSummaryAccumulator::localDayAndHour(qint64 seconds) { | |
| 60 |
1/2✓ Branch 2 → 3 taken 101083 times.
✗ Branch 2 → 4 not taken.
|
101083 | const qint64 utcHour = seconds >= 0 ? seconds / 3600 : (seconds - 3599) / 3600; |
| 61 | 101083 | auto offset = m_offsetByUtcHour.constFind(utcHour); | |
| 62 |
2/2✓ Branch 8 → 9 taken 94199 times.
✓ Branch 8 → 15 taken 6884 times.
|
101083 | if (offset == m_offsetByUtcHour.constEnd()) |
| 63 |
3/3✓ Branch 9 → 10 taken 94199 times.
✓ Branch 10 → 11 taken 94199 times.
✓ Branch 11 → 12 taken 94199 times.
|
94199 | offset = m_offsetByUtcHour.insert(utcHour, QDateTime::fromSecsSinceEpoch(utcHour * 3600).offsetFromUtc()); |
| 64 | 101083 | const qint64 local = seconds + offset.value(); | |
| 65 |
1/2✓ Branch 16 → 17 taken 101083 times.
✗ Branch 16 → 18 not taken.
|
101083 | const qint64 days = local >= 0 ? local / 86400 : (local - 86399) / 86400; |
| 66 | 101083 | const qint64 secondOfDay = local - days * 86400; | |
| 67 |
2/2✓ Branch 19 → 20 taken 101083 times.
✓ Branch 20 → 21 taken 101083 times.
|
101083 | return {QDate(1970, 1, 1).addDays(days), int(secondOfDay / 3600)}; |
| 68 | } | ||
| 69 | |||
| 70 | // Address headers repeat (a sender writes many mails): each distinct header | ||
| 71 | // is parsed once per summary; the cache is bounded. | ||
| 72 | 143442 | const QList<QPair<QString, QString>> &MailSearchSummaryAccumulator::cachedAddresses(const QString &header) { | |
| 73 | 143442 | auto found = m_addressCache.constFind(header); | |
| 74 |
2/2✓ Branch 5 → 6 taken 142383 times.
✓ Branch 5 → 7 taken 1059 times.
|
143442 | if (found != m_addressCache.constEnd()) return found.value(); |
| 75 |
1/2✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 1059 times.
|
1059 | if (m_addressCache.size() >= MaximumCachedHeaders) m_addressCache.clear(); |
| 76 |
2/2✓ Branch 10 → 11 taken 1059 times.
✓ Branch 11 → 12 taken 1059 times.
|
1059 | return m_addressCache.insert(header, addresses(header)).value(); |
| 77 | } | ||
| 78 | |||
| 79 | 105426 | bool MailSearchSummaryAccumulator::add(const MailSearchSummaryRow &row) { | |
| 80 |
1/1✓ Branch 2 → 3 taken 105426 times.
|
105426 | countFolder(row); |
| 81 |
2/2✓ Branch 4 → 5 taken 4343 times.
✓ Branch 4 → 6 taken 101083 times.
|
105426 | if (m_seen.contains(row.identity)) return false; |
| 82 |
1/1✓ Branch 6 → 7 taken 101083 times.
|
101083 | m_seen.insert(row.identity); |
| 83 | 101083 | ++m_total; | |
| 84 |
1/2✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 101083 times.
|
101083 | if (!row.hasDate) { |
| 85 | ✗ | ++m_undated; | |
| 86 | } else { | ||
| 87 |
1/1✓ Branch 9 → 10 taken 101083 times.
|
101083 | const auto [day, hour] = localDayAndHour(row.date); |
| 88 |
1/1✓ Branch 12 → 13 taken 101083 times.
|
101083 | ++m_days[day]; |
| 89 |
7/8✓ Branch 13 → 14 taken 101083 times.
✓ Branch 14 → 15 taken 247 times.
✓ Branch 14 → 18 taken 100836 times.
✓ Branch 16 → 17 taken 247 times.
✗ Branch 16 → 18 not taken.
✓ Branch 19 → 20 taken 247 times.
✓ Branch 19 → 22 taken 100836 times.
✓ Branch 20 → 21 taken 247 times.
|
101083 | if (m_hoursDay.isValid() && day == m_hoursDay) ++m_hours[hour]; |
| 90 | } | ||
| 91 |
2/2✓ Branch 23 → 24 taken 29362 times.
✓ Branch 23 → 25 taken 71721 times.
|
101083 | if (m_detail == MailSearchSummary::Detail::CountOnly) return true; |
| 92 |
2/2✓ Branch 25 → 26 taken 12986 times.
✓ Branch 25 → 27 taken 58735 times.
|
71721 | if (!(row.flags & MailFlag::Seen)) ++m_unread; |
| 93 |
2/2✓ Branch 27 → 28 taken 11185 times.
✓ Branch 27 → 29 taken 60536 times.
|
71721 | if (row.flags & MailFlag::Flagged) ++m_flagged; |
| 94 |
2/2✓ Branch 29 → 30 taken 3901 times.
✓ Branch 29 → 31 taken 67820 times.
|
71721 | if (row.flags & MailFlag::Answered) ++m_answered; |
| 95 |
2/2✓ Branch 31 → 32 taken 9926 times.
✓ Branch 31 → 33 taken 61795 times.
|
71721 | if (row.hasAttachments) ++m_attachments; |
| 96 |
1/1✓ Branch 33 → 34 taken 71721 times.
|
71721 | const auto senders = cachedAddresses(row.from); |
| 97 |
2/3✓ Branch 36 → 37 taken 71721 times.
✗ Branch 36 → 40 not taken.
✓ Branch 39 → 40 taken 71721 times.
|
71721 | if (!senders.isEmpty()) count(m_senders, senders.first().first, senders.first().second); |
| 98 | 71721 | QSet<QString> recipients; | |
| 99 |
3/3✓ Branch 41 → 42 taken 71721 times.
✓ Branch 54 → 44 taken 99230 times.
✓ Branch 54 → 55 taken 71721 times.
|
170951 | for (const auto &[address, name] : cachedAddresses(row.recipients)) { |
| 100 |
1/2✗ Branch 48 → 49 not taken.
✓ Branch 48 → 50 taken 99230 times.
|
99230 | if (recipients.contains(address)) continue; |
| 101 |
1/1✓ Branch 50 → 51 taken 99230 times.
|
99230 | recipients.insert(address); |
| 102 |
1/1✓ Branch 51 → 52 taken 99230 times.
|
99230 | count(m_recipients, address, name); |
| 103 | } | ||
| 104 |
3/3✓ Branch 58 → 59 taken 9089 times.
✓ Branch 61 → 57 taken 9089 times.
✓ Branch 61 → 62 taken 71721 times.
|
80810 | for (const QString &label : row.labels) count(m_labels, label, label); |
| 105 |
1/1✓ Branch 62 → 63 taken 71721 times.
|
71721 | QString list = row.listId.trimmed(); |
| 106 |
3/9✓ Branch 65 → 66 taken 71721 times.
✗ Branch 66 → 67 not taken.
✓ Branch 66 → 72 taken 71721 times.
✗ Branch 69 → 70 not taken.
✗ Branch 70 → 71 not taken.
✗ Branch 70 → 72 not taken.
✗ Branch 73 → 74 not taken.
✓ Branch 73 → 79 taken 71721 times.
✗ Branch 75 → 76 not taken.
|
71721 | if (list.startsWith(QLatin1Char('<')) && list.endsWith(QLatin1Char('>'))) list = list.mid(1, list.size() - 2); |
| 107 |
1/1✓ Branch 79 → 80 taken 71721 times.
|
71721 | count(m_lists, list, list); |
| 108 | 71721 | return true; | |
| 109 | 71721 | } | |
| 110 | |||
| 111 | 2570 | QList<MailSearchFacetValue> MailSearchSummaryAccumulator::top(const QHash<QString, Facet> &facet) { | |
| 112 | 2570 | QList<MailSearchFacetValue> values; | |
| 113 |
1/1✓ Branch 3 → 4 taken 2570 times.
|
2570 | values.reserve(facet.size()); |
| 114 |
2/2✓ Branch 15 → 5 taken 2221 times.
✓ Branch 15 → 16 taken 2570 times.
|
4791 | for (auto it = facet.cbegin(); it != facet.cend(); ++it) values.append({it.key(), it->label, it->count}); |
| 115 |
3/3✓ Branch 16 → 17 taken 2570 times.
✓ Branch 17 → 18 taken 2570 times.
✓ Branch 18 → 19 taken 2570 times.
|
2570 | std::sort(values.begin(), values.end(), [](const MailSearchFacetValue &a, const MailSearchFacetValue &b) { |
| 116 |
2/2✓ Branch 2 → 3 taken 969 times.
✓ Branch 2 → 4 taken 529 times.
|
1498 | if (a.count != b.count) return a.count > b.count; |
| 117 | 529 | const int byLabel = QString::compare(a.label, b.label, Qt::CaseInsensitive); | |
| 118 |
1/2✓ Branch 5 → 6 taken 529 times.
✗ Branch 5 → 7 not taken.
|
529 | return byLabel != 0 ? byLabel < 0 : a.value < b.value; |
| 119 | }); | ||
| 120 | 2570 | return values; | |
| 121 | 2221 | } | |
| 122 | |||
| 123 | 514 | void MailSearchSummaryAccumulator::finish(MailSearchSummary &summary) const { | |
| 124 | 514 | summary.total = m_total; | |
| 125 | 514 | summary.undated = m_undated; | |
| 126 | 514 | summary.dayCounts = m_days; | |
| 127 | 514 | summary.hourCounts = m_hours; | |
| 128 |
5/5✓ Branch 4 → 5 taken 514 times.
✓ Branch 5 → 6 taken 51 times.
✓ Branch 5 → 7 taken 463 times.
✓ Branch 6 → 9 taken 51 times.
✓ Branch 7 → 8 taken 463 times.
|
514 | summary.firstDate = m_days.isEmpty() ? QDate() : m_days.firstKey(); |
| 129 |
5/5✓ Branch 9 → 10 taken 514 times.
✓ Branch 10 → 11 taken 51 times.
✓ Branch 10 → 12 taken 463 times.
✓ Branch 11 → 14 taken 51 times.
✓ Branch 12 → 13 taken 463 times.
|
514 | summary.lastDate = m_days.isEmpty() ? QDate() : m_days.lastKey(); |
| 130 | 514 | summary.unread = m_unread; | |
| 131 | 514 | summary.flagged = m_flagged; | |
| 132 | 514 | summary.answered = m_answered; | |
| 133 | 514 | summary.withAttachment = m_attachments; | |
| 134 |
2/2✓ Branch 14 → 15 taken 514 times.
✓ Branch 15 → 16 taken 514 times.
|
514 | summary.topSenders = top(m_senders).mid(0, MailSearchSummary::TopValues); |
| 135 |
2/2✓ Branch 19 → 20 taken 514 times.
✓ Branch 20 → 21 taken 514 times.
|
514 | summary.topRecipients = top(m_recipients).mid(0, MailSearchSummary::TopValues); |
| 136 |
1/1✓ Branch 24 → 25 taken 514 times.
|
514 | summary.folders = top(m_folders); |
| 137 |
1/1✓ Branch 27 → 28 taken 514 times.
|
514 | summary.topFolders = summary.folders.mid(0, MailSearchSummary::TopValues); |
| 138 |
2/2✓ Branch 30 → 31 taken 514 times.
✓ Branch 31 → 32 taken 514 times.
|
514 | summary.topLabels = top(m_labels).mid(0, TopLabelValues); |
| 139 |
2/2✓ Branch 35 → 36 taken 514 times.
✓ Branch 36 → 37 taken 514 times.
|
514 | summary.topLists = top(m_lists).mid(0, MailSearchSummary::TopValues); |
| 140 | 514 | } | |
| 141 | |||
| 142 | namespace { | ||
| 143 | |||
| 144 | 515 | QString summarySql(const QStringList &where, bool full) { | |
| 145 | 1030 | const QString columns = QStringLiteral("h.id, h.date, h.message_id, fo.path") + | |
| 146 |
7/11✓ Branch 2 → 3 taken 366 times.
✓ Branch 2 → 8 taken 149 times.
✓ Branch 7 → 9 taken 366 times.
✓ Branch 16 → 17 taken 366 times.
✓ Branch 16 → 18 taken 149 times.
✓ Branch 18 → 19 taken 366 times.
✓ Branch 18 → 20 taken 149 times.
✗ Branch 62 → 63 not taken.
✗ Branch 62 → 64 not taken.
✗ Branch 66 → 67 not taken.
✗ Branch 66 → 68 not taken.
|
1396 | (full ? QStringLiteral(", substr(h.from_addr,1,%1), " |
| 147 | "substr(coalesce(h.to_addr,'') || ', ' || coalesce(h.cc_addr,''),1,%1), h.flags, " | ||
| 148 | "h.has_attachments, h.list_id, " | ||
| 149 | "(SELECT group_concat(label, char(31)) FROM mail_labels WHERE header_id = h.id)") | ||
| 150 | .arg(MaximumAddressCharacters) | ||
| 151 |
1/1✓ Branch 12 → 13 taken 515 times.
|
515 | : QString()); |
| 152 |
1/1✓ Branch 33 → 34 taken 515 times.
|
1030 | return QStringLiteral("SELECT ") + columns + |
| 153 |
1/1✓ Branch 34 → 35 taken 515 times.
|
2060 | QStringLiteral(" FROM mail_fts f JOIN headers h ON h.id = f.rowid JOIN folders fo ON fo.id = h.folder_id WHERE ") + |
| 154 |
3/3✓ Branch 26 → 27 taken 515 times.
✓ Branch 35 → 36 taken 515 times.
✓ Branch 36 → 37 taken 515 times.
|
2575 | where.join(QStringLiteral(" AND ")) + QStringLiteral(" ORDER BY h.date DESC, h.id DESC"); |
| 155 | 515 | } | |
| 156 | |||
| 157 | 105470 | MailSearchSummaryRow summaryRow(const QSqlQuery &q, bool full) { | |
| 158 | 105470 | MailSearchSummaryRow row; | |
| 159 |
2/2✓ Branch 2 → 3 taken 105470 times.
✓ Branch 3 → 4 taken 105470 times.
|
105470 | row.id = q.value(0).toLongLong(); |
| 160 |
2/2✓ Branch 5 → 6 taken 105470 times.
✓ Branch 6 → 7 taken 105470 times.
|
105470 | row.hasDate = !q.value(1).isNull(); |
| 161 |
2/2✓ Branch 8 → 9 taken 105470 times.
✓ Branch 9 → 10 taken 105470 times.
|
105470 | row.date = q.value(1).toLongLong(); |
| 162 |
3/3✓ Branch 11 → 12 taken 105470 times.
✓ Branch 12 → 13 taken 105470 times.
✓ Branch 13 → 14 taken 105470 times.
|
105470 | const QString messageId = MessageId::canonicalFirst(q.value(2).toString()); |
| 163 |
10/16✓ Branch 17 → 18 taken 10271 times.
✓ Branch 17 → 23 taken 95199 times.
✓ Branch 18 → 19 taken 10271 times.
✓ Branch 22 → 24 taken 10271 times.
✓ Branch 26 → 27 taken 10271 times.
✓ Branch 26 → 28 taken 95199 times.
✓ Branch 28 → 29 taken 10271 times.
✓ Branch 28 → 30 taken 95199 times.
✓ Branch 30 → 31 taken 10271 times.
✓ Branch 30 → 32 taken 95199 times.
✗ Branch 90 → 91 not taken.
✗ Branch 90 → 92 not taken.
✗ Branch 94 → 95 not taken.
✗ Branch 94 → 96 not taken.
✗ Branch 98 → 99 not taken.
✗ Branch 98 → 100 not taken.
|
115741 | row.identity = messageId.isEmpty() ? QStringLiteral("row:") + QString::number(row.id) : messageId; |
| 164 |
2/2✓ Branch 32 → 33 taken 105470 times.
✓ Branch 33 → 34 taken 105470 times.
|
105470 | row.folderPath = q.value(3).toString(); |
| 165 |
2/2✓ Branch 37 → 38 taken 31421 times.
✓ Branch 37 → 39 taken 74049 times.
|
105470 | if (!full) return row; |
| 166 |
2/2✓ Branch 39 → 40 taken 74049 times.
✓ Branch 40 → 41 taken 74049 times.
|
74049 | row.from = q.value(4).toString(); |
| 167 |
2/2✓ Branch 44 → 45 taken 74049 times.
✓ Branch 45 → 46 taken 74049 times.
|
74049 | row.recipients = q.value(5).toString(); |
| 168 |
2/2✓ Branch 49 → 50 taken 74049 times.
✓ Branch 50 → 51 taken 74049 times.
|
74049 | row.flags = q.value(6).toUInt(); |
| 169 |
2/2✓ Branch 52 → 53 taken 74049 times.
✓ Branch 53 → 54 taken 74049 times.
|
74049 | row.hasAttachments = q.value(7).toBool(); |
| 170 |
2/2✓ Branch 55 → 56 taken 74049 times.
✓ Branch 56 → 57 taken 74049 times.
|
74049 | row.listId = q.value(8).toString(); |
| 171 |
2/2✓ Branch 60 → 61 taken 74049 times.
✓ Branch 61 → 62 taken 74049 times.
|
74049 | const QString labels = q.value(9).toString(); |
| 172 |
3/3✓ Branch 64 → 65 taken 9119 times.
✓ Branch 64 → 71 taken 64930 times.
✓ Branch 67 → 68 taken 9119 times.
|
74049 | if (!labels.isEmpty()) row.labels = labels.split(QChar(31), Qt::SkipEmptyParts); |
| 173 | 74049 | return row; | |
| 174 | 105470 | } | |
| 175 | |||
| 176 | } // namespace | ||
| 177 | |||
| 178 | 523 | MailSearchSummary MailSearchIndex::searchSummary(const QString &query, const SearchFilter &filter, | |
| 179 | const std::optional<QString> &accountId, | ||
| 180 | MailSearchSummary::Detail detail, | ||
| 181 | const std::atomic_bool &cancelled, int timeLimitMs, | ||
| 182 | const QSet<QString> &allowedFolders) const { | ||
| 183 | 523 | MailSearchSummary summary; | |
| 184 |
7/9✓ Branch 2 → 3 taken 523 times.
✓ Branch 3 → 4 taken 522 times.
✓ Branch 3 → 9 taken 1 time.
✓ Branch 5 → 6 taken 522 times.
✗ Branch 5 → 10 not taken.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 522 times.
✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 19 taken 522 times.
|
523 | if (!filter.fitsReadLimits(query) || (accountId && accountId->size() > 4096)) { |
| 185 | 1 | summary.outcome = MailSearchSummary::Outcome::Limited; | |
| 186 | 1 | summary.error = QStringLiteral("Search request exceeds the bounded summary contract"); | |
| 187 | 1 | return summary; | |
| 188 | } | ||
| 189 |
2/3✓ Branch 19 → 20 taken 522 times.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 28 taken 522 times.
|
522 | if (!m_db.isOpen()) { summary.error = QStringLiteral("Search database is not open"); return summary; } |
| 190 |
2/2✓ Branch 29 → 30 taken 6 times.
✓ Branch 29 → 31 taken 516 times.
|
522 | if (cancelled.load()) { summary.outcome = MailSearchSummary::Outcome::Cancelled; return summary; } |
| 191 | 516 | QStringList matchTerms, likeTerms; | |
| 192 |
1/1✓ Branch 31 → 32 taken 516 times.
|
516 | splitSearchTerms(query, matchTerms, likeTerms); |
| 193 |
9/9✓ Branch 33 → 34 taken 246 times.
✓ Branch 33 → 39 taken 270 times.
✓ Branch 35 → 36 taken 226 times.
✓ Branch 35 → 39 taken 20 times.
✓ Branch 36 → 37 taken 226 times.
✓ Branch 37 → 38 taken 1 time.
✓ Branch 37 → 39 taken 225 times.
✓ Branch 40 → 41 taken 1 time.
✓ Branch 40 → 42 taken 515 times.
|
516 | if (matchTerms.isEmpty() && likeTerms.isEmpty() && filter.isEmpty()) { |
| 194 | 1 | summary.outcome = MailSearchSummary::Outcome::Ready; // no search: nothing matches (as searchPage) | |
| 195 | 1 | return summary; | |
| 196 | } | ||
| 197 |
1/1✓ Branch 42 → 43 taken 515 times.
|
515 | SqliteInterruptScope interrupt(m_db, cancelled, timeLimitMs); |
| 198 |
1/2✗ Branch 44 → 45 not taken.
✓ Branch 44 → 52 taken 515 times.
|
515 | if (!interrupt.valid()) { |
| 199 | ✗ | summary.error = QStringLiteral("Search reader requires an interruptible SQLite connection"); | |
| 200 | ✗ | return summary; | |
| 201 | } | ||
| 202 | 515 | QStringList folderPatterns; | |
| 203 |
2/2✓ Branch 62 → 54 taken 43 times.
✓ Branch 62 → 63 taken 515 times.
|
558 | for (const QString &pattern : filter.folderPatterns) |
| 204 |
3/4✓ Branch 55 → 56 taken 43 times.
✓ Branch 58 → 59 taken 43 times.
✗ Branch 58 → 60 not taken.
✓ Branch 59 → 60 taken 43 times.
|
43 | if (!pattern.trimmed().isEmpty()) folderPatterns.append(pattern); |
| 205 | 515 | const bool full = detail == MailSearchSummary::Detail::Full; | |
| 206 |
1/1✓ Branch 63 → 64 taken 515 times.
|
515 | QSqlQuery q(m_db); |
| 207 |
1/1✓ Branch 64 → 65 taken 515 times.
|
515 | q.setForwardOnly(true); |
| 208 |
5/5✓ Branch 67 → 68 taken 515 times.
✓ Branch 68 → 69 taken 515 times.
✓ Branch 69 → 70 taken 515 times.
✓ Branch 72 → 73 taken 1 time.
✓ Branch 72 → 79 taken 514 times.
|
515 | if (!q.prepare(summarySql(searchConditions(filter, accountId.has_value(), !matchTerms.isEmpty(), likeTerms, |
| 209 | folderPatterns), full))) { | ||
| 210 |
2/2✓ Branch 73 → 74 taken 1 time.
✓ Branch 74 → 75 taken 1 time.
|
1 | summary.error = q.lastError().text(); |
| 211 | 1 | return summary; | |
| 212 | } | ||
| 213 |
2/2✓ Branch 81 → 82 taken 514 times.
✓ Branch 82 → 83 taken 514 times.
|
514 | bindSearchParameters(q, filter, accountId, matchTerms.join(QLatin1Char(' ')), likeTerms, folderPatterns, 0, 0); |
| 214 |
6/6✓ Branch 84 → 85 taken 514 times.
✓ Branch 86 → 87 taken 110 times.
✓ Branch 87 → 88 taken 104 times.
✓ Branch 87 → 94 taken 6 times.
✓ Branch 91 → 92 taken 9 times.
✓ Branch 91 → 94 taken 95 times.
|
728 | const QDate hoursDay = filter.dateFrom.isValid() && filter.dateTo.isValid() && |
| 215 |
2/2✓ Branch 88 → 89 taken 104 times.
✓ Branch 89 → 90 taken 104 times.
|
104 | filter.dateFrom.date() == filter.dateTo.date() |
| 216 |
4/4✓ Branch 85 → 86 taken 110 times.
✓ Branch 85 → 94 taken 404 times.
✓ Branch 92 → 93 taken 9 times.
✓ Branch 94 → 95 taken 505 times.
|
624 | ? filter.dateFrom.date() : QDate(); |
| 217 |
1/1✓ Branch 95 → 96 taken 514 times.
|
514 | MailSearchSummaryAccumulator accumulator(detail, hoursDay); |
| 218 |
1/1✓ Branch 96 → 97 taken 514 times.
|
514 | const bool executed = q.exec(); |
| 219 | 514 | bool exhausted = false; // every row was read (not stopped by a limit) | |
| 220 |
6/6✓ Branch 118 → 119 taken 105966 times.
✓ Branch 118 → 122 taken 18 times.
✓ Branch 120 → 121 taken 105962 times.
✓ Branch 120 → 122 taken 4 times.
✓ Branch 123 → 98 taken 105962 times.
✓ Branch 123 → 124 taken 22 times.
|
105984 | while (executed && !interrupt.stopped()) { |
| 221 |
3/3✓ Branch 98 → 99 taken 105962 times.
✓ Branch 99 → 100 taken 492 times.
✓ Branch 99 → 101 taken 105470 times.
|
105962 | if (!q.next()) { exhausted = true; break; } |
| 222 |
1/1✓ Branch 101 → 102 taken 105470 times.
|
105470 | MailSearchSummaryRow row = summaryRow(q, full); |
| 223 | // The list admits results of folders the account lists only; so do the counts. | ||
| 224 |
6/6✓ Branch 103 → 104 taken 47682 times.
✓ Branch 103 → 107 taken 57788 times.
✓ Branch 105 → 106 taken 44 times.
✓ Branch 105 → 107 taken 47638 times.
✓ Branch 108 → 109 taken 44 times.
✓ Branch 108 → 110 taken 105426 times.
|
105470 | if (!allowedFolders.isEmpty() && !allowedFolders.contains(row.folderPath)) continue; |
| 225 |
1/1✓ Branch 110 → 111 taken 105426 times.
|
105426 | accumulator.add(row); |
| 226 | 105470 | } | |
| 227 |
1/1✓ Branch 124 → 125 taken 514 times.
|
514 | accumulator.finish(summary); |
| 228 | 514 | summary.elapsedMs = interrupt.elapsedMs(); | |
| 229 |
2/2✓ Branch 127 → 128 taken 21 times.
✓ Branch 127 → 131 taken 493 times.
|
514 | if (cancelled.load()) { |
| 230 | 21 | summary = {}; | |
| 231 | 21 | summary.outcome = MailSearchSummary::Outcome::Cancelled; | |
| 232 |
6/8✓ Branch 132 → 133 taken 492 times.
✓ Branch 132 → 135 taken 1 time.
✓ Branch 133 → 134 taken 492 times.
✗ Branch 133 → 136 not taken.
✗ Branch 134 → 135 not taken.
✓ Branch 134 → 136 taken 492 times.
✓ Branch 137 → 138 taken 1 time.
✓ Branch 137 → 145 taken 492 times.
|
493 | } else if (interrupt.timedOut() || (executed && !exhausted)) { |
| 233 | 1 | summary.outcome = MailSearchSummary::Outcome::Limited; | |
| 234 | 1 | summary.error = QStringLiteral("Search summary exceeded the worker time limit"); | |
| 235 |
6/12✓ Branch 145 → 146 taken 492 times.
✗ Branch 145 → 149 not taken.
✓ Branch 146 → 147 taken 492 times.
✓ Branch 147 → 148 taken 492 times.
✗ Branch 148 → 149 not taken.
✓ Branch 148 → 150 taken 492 times.
✓ Branch 151 → 152 taken 492 times.
✗ Branch 151 → 153 not taken.
✗ Branch 153 → 154 not taken.
✓ Branch 153 → 175 taken 492 times.
✗ Branch 206 → 207 not taken.
✗ Branch 206 → 208 not taken.
|
492 | } else if (!executed || q.lastError().isValid()) { |
| 236 | ✗ | qCWarning(lcCache) << "Search summary failed:" << q.lastError().text(); | |
| 237 | ✗ | summary.outcome = MailSearchSummary::Outcome::Failed; | |
| 238 | ✗ | summary.error = q.lastError().text(); | |
| 239 | } else { | ||
| 240 | 492 | summary.outcome = MailSearchSummary::Outcome::Ready; | |
| 241 | } | ||
| 242 | 514 | return summary; | |
| 243 | 516 | } | |
| 244 |