data/CalendarStoreSync.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // CalendarStore: sync enumeration checkpoint (Sprint 96 S96.3.0, | ||
| 2 | // CAL-SYNC-TRUNCATION). Callers run these inside the page transaction of | ||
| 3 | // CalendarSyncApply, so a page, its seen UIDs and its checkpoint commit | ||
| 4 | // together or not at all. | ||
| 5 | #include "CalendarStore.h" | ||
| 6 | |||
| 7 | #include <QLoggingCategory> | ||
| 8 | #include <QSqlError> | ||
| 9 | #include <QSqlQuery> | ||
| 10 | |||
| 11 | Q_DECLARE_LOGGING_CATEGORY(lcCalStore) | ||
| 12 | |||
| 13 | 373 | CalendarStore::EnumerationState CalendarStore::enumerationState(const QString &accountId, | |
| 14 | const QString &path) const { | ||
| 15 |
1/1✓ Branch 2 → 3 taken 373 times.
|
373 | QSqlQuery q(m_db); |
| 16 |
1/1✓ Branch 6 → 7 taken 373 times.
|
373 | q.prepare(QStringLiteral("SELECT enum_active, enum_token FROM calendars WHERE accountId = ? AND path = ?")); |
| 17 |
1/1✓ Branch 11 → 12 taken 373 times.
|
373 | q.addBindValue(accountId); |
| 18 |
1/1✓ Branch 15 → 16 taken 373 times.
|
373 | q.addBindValue(path); |
| 19 |
7/8✓ Branch 17 → 18 taken 373 times.
✓ Branch 18 → 19 taken 372 times.
✓ Branch 18 → 21 taken 1 time.
✓ Branch 19 → 20 taken 372 times.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 372 times.
✓ Branch 23 → 24 taken 1 time.
✓ Branch 23 → 25 taken 372 times.
|
373 | if (!q.exec() || !q.next()) |
| 20 | 1 | return {}; | |
| 21 |
2/2✓ Branch 25 → 26 taken 372 times.
✓ Branch 26 → 27 taken 372 times.
|
744 | return {q.value(0).toInt() != 0, q.value(1).toString()}; |
| 22 | 745 | } | |
| 23 | |||
| 24 | 113 | bool CalendarStore::beginEnumeration(const QString &accountId, const QString &path) { | |
| 25 |
1/1✓ Branch 2 → 3 taken 113 times.
|
113 | const qint64 calId = calendarIdForPath(accountId, path); |
| 26 |
2/2✓ Branch 3 → 4 taken 2 times.
✓ Branch 3 → 5 taken 111 times.
|
113 | if (calId < 0) |
| 27 | 2 | return false; | |
| 28 |
1/1✓ Branch 5 → 6 taken 111 times.
|
111 | QSqlQuery clear(m_db); |
| 29 |
1/1✓ Branch 9 → 10 taken 111 times.
|
111 | clear.prepare(QStringLiteral("DELETE FROM calendar_enum_seen WHERE calendar_id = ?")); |
| 30 |
1/1✓ Branch 14 → 15 taken 111 times.
|
111 | clear.addBindValue(calId); |
| 31 |
1/1✓ Branch 16 → 17 taken 111 times.
|
111 | QSqlQuery state(m_db); |
| 32 |
1/1✓ Branch 20 → 21 taken 111 times.
|
111 | state.prepare(QStringLiteral("UPDATE calendars SET enum_active = 1, enum_token = '' WHERE id = ?")); |
| 33 |
1/1✓ Branch 25 → 26 taken 111 times.
|
111 | state.addBindValue(calId); |
| 34 |
5/8✓ Branch 27 → 28 taken 111 times.
✓ Branch 28 → 29 taken 111 times.
✗ Branch 28 → 31 not taken.
✓ Branch 29 → 30 taken 111 times.
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 32 taken 111 times.
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 55 taken 111 times.
|
111 | if (!clear.exec() || !state.exec()) { |
| 35 | ✗ | qCWarning(lcCalStore) << "beginEnumeration:" << clear.lastError().text() << state.lastError().text(); | |
| 36 | ✗ | return false; | |
| 37 | } | ||
| 38 | 111 | return true; | |
| 39 | 111 | } | |
| 40 | |||
| 41 | 219 | bool CalendarStore::addEnumerationSeen(const QString &accountId, const QString &path, | |
| 42 | const QStringList &uids) { | ||
| 43 |
1/1✓ Branch 2 → 3 taken 219 times.
|
219 | const qint64 calId = calendarIdForPath(accountId, path); |
| 44 |
2/2✓ Branch 3 → 4 taken 2 times.
✓ Branch 3 → 5 taken 217 times.
|
219 | if (calId < 0) |
| 45 | 2 | return false; | |
| 46 |
1/1✓ Branch 5 → 6 taken 217 times.
|
217 | QSqlQuery q(m_db); |
| 47 |
1/1✓ Branch 9 → 10 taken 217 times.
|
217 | q.prepare(QStringLiteral("INSERT OR IGNORE INTO calendar_enum_seen (calendar_id, uid) VALUES (?, ?)")); |
| 48 |
2/2✓ Branch 46 → 14 taken 430 times.
✓ Branch 46 → 47 taken 217 times.
|
647 | for (const QString &uid : uids) { |
| 49 |
2/2✓ Branch 16 → 17 taken 1 time.
✓ Branch 16 → 18 taken 429 times.
|
430 | if (uid.isEmpty()) |
| 50 | 1 | continue; | |
| 51 |
1/1✓ Branch 20 → 21 taken 429 times.
|
429 | q.addBindValue(calId); |
| 52 |
1/1✓ Branch 24 → 25 taken 429 times.
|
429 | q.addBindValue(uid); |
| 53 |
2/3✓ Branch 26 → 27 taken 429 times.
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 44 taken 429 times.
|
429 | if (!q.exec()) { |
| 54 | ✗ | qCWarning(lcCalStore) << "addEnumerationSeen:" << q.lastError().text(); | |
| 55 | ✗ | return false; | |
| 56 | } | ||
| 57 | } | ||
| 58 | 217 | return true; | |
| 59 | 217 | } | |
| 60 | |||
| 61 | 6 | bool CalendarStore::setEnumerationCheckpoint(const QString &accountId, const QString &path, | |
| 62 | const QString &token) { | ||
| 63 |
1/1✓ Branch 2 → 3 taken 6 times.
|
6 | QSqlQuery q(m_db); |
| 64 |
1/1✓ Branch 6 → 7 taken 6 times.
|
6 | q.prepare(QStringLiteral("UPDATE calendars SET enum_token = ? WHERE accountId = ? AND path = ? AND enum_active = 1")); |
| 65 |
1/1✓ Branch 11 → 12 taken 6 times.
|
6 | q.addBindValue(token); |
| 66 |
1/1✓ Branch 15 → 16 taken 6 times.
|
6 | q.addBindValue(accountId); |
| 67 |
1/1✓ Branch 19 → 20 taken 6 times.
|
6 | q.addBindValue(path); |
| 68 |
3/3✓ Branch 21 → 22 taken 6 times.
✓ Branch 22 → 23 taken 1 time.
✓ Branch 22 → 39 taken 5 times.
|
6 | if (!q.exec()) { |
| 69 |
8/8✓ Branch 23 → 24 taken 1 time.
✓ Branch 27 → 28 taken 1 time.
✓ Branch 28 → 29 taken 1 time.
✓ Branch 29 → 30 taken 1 time.
✓ Branch 30 → 31 taken 1 time.
✓ Branch 31 → 32 taken 1 time.
✓ Branch 37 → 25 taken 1 time.
✓ Branch 37 → 38 taken 1 time.
|
2 | qCWarning(lcCalStore) << "setEnumerationCheckpoint:" << q.lastError().text(); |
| 70 | 1 | return false; | |
| 71 | } | ||
| 72 |
1/1✓ Branch 39 → 40 taken 5 times.
|
5 | return q.numRowsAffected() == 1; |
| 73 | 6 | } | |
| 74 | |||
| 75 | 110 | bool CalendarStore::finishEnumeration(const QString &accountId, const QString &path, | |
| 76 | const QStringList &keepUids, bool completeEmpty) { | ||
| 77 |
1/1✓ Branch 2 → 3 taken 110 times.
|
110 | const qint64 calId = calendarIdForPath(accountId, path); |
| 78 |
2/2✓ Branch 3 → 4 taken 2 times.
✓ Branch 3 → 5 taken 108 times.
|
110 | if (calId < 0) |
| 79 | 2 | return false; | |
| 80 |
1/1✓ Branch 5 → 6 taken 108 times.
|
108 | QSqlQuery count(m_db); |
| 81 |
1/1✓ Branch 9 → 10 taken 108 times.
|
108 | count.prepare(QStringLiteral("SELECT COUNT(*) FROM calendar_enum_seen WHERE calendar_id = ?")); |
| 82 |
1/1✓ Branch 14 → 15 taken 108 times.
|
108 | count.addBindValue(calId); |
| 83 |
5/8✓ Branch 16 → 17 taken 108 times.
✓ Branch 17 → 18 taken 108 times.
✗ Branch 17 → 20 not taken.
✓ Branch 18 → 19 taken 108 times.
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 108 times.
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 24 taken 108 times.
|
108 | if (!count.exec() || !count.next()) |
| 84 | ✗ | return false; | |
| 85 |
2/2✓ Branch 24 → 25 taken 108 times.
✓ Branch 25 → 26 taken 108 times.
|
108 | const bool sawResources = count.value(0).toLongLong() > 0; |
| 86 |
2/2✓ Branch 27 → 28 taken 19 times.
✓ Branch 27 → 75 taken 89 times.
|
108 | if (sawResources) { |
| 87 |
2/3✓ Branch 28 → 29 taken 19 times.
✗ Branch 29 → 30 not taken.
✓ Branch 29 → 31 taken 19 times.
|
19 | if (!addEnumerationSeen(accountId, path, keepUids)) |
| 88 | ✗ | return false; | |
| 89 |
2/2✓ Branch 73 → 33 taken 57 times.
✓ Branch 73 → 74 taken 19 times.
|
76 | for (const auto *table : {"events", "tasks", "calendar_resources"}) { |
| 90 |
1/1✓ Branch 33 → 34 taken 57 times.
|
57 | QSqlQuery prune(m_db); |
| 91 |
1/1✓ Branch 40 → 41 taken 57 times.
|
171 | prune.prepare(QStringLiteral("DELETE FROM %1 WHERE calendar_id = ? AND NOT EXISTS " |
| 92 | "(SELECT 1 FROM calendar_enum_seen seen WHERE seen.calendar_id = %1.calendar_id " | ||
| 93 | "AND seen.uid = %1.uid)") | ||
| 94 |
1/1✓ Branch 39 → 40 taken 57 times.
|
171 | .arg(QLatin1String(table))); |
| 95 |
1/1✓ Branch 46 → 47 taken 57 times.
|
57 | prune.addBindValue(calId); |
| 96 |
2/3✓ Branch 48 → 49 taken 57 times.
✗ Branch 49 → 50 not taken.
✓ Branch 49 → 67 taken 57 times.
|
57 | if (!prune.exec()) { |
| 97 | ✗ | qCWarning(lcCalStore) << "finishEnumeration prune" << table << prune.lastError().text(); | |
| 98 | ✗ | return false; | |
| 99 | } | ||
| 100 | 57 | } | |
| 101 |
2/2✓ Branch 75 → 76 taken 87 times.
✓ Branch 75 → 123 taken 2 times.
|
89 | } else if (completeEmpty) { |
| 102 | // F-59 (SPRINT-104): a positively completed full enumeration that saw | ||
| 103 | // nothing means the collection is empty — prune the server-owned rows | ||
| 104 | // (the caller's queued UIDs arrive as keepUids and survive) instead of | ||
| 105 | // certifying stale events and tasks as synchronized. The nothing-seen | ||
| 106 | // guard stays for enumerations without the completeness flag (a | ||
| 107 | // partial or interrupted listing). | ||
| 108 |
2/3✓ Branch 76 → 77 taken 87 times.
✗ Branch 77 → 78 not taken.
✓ Branch 77 → 79 taken 87 times.
|
87 | if (!addEnumerationSeen(accountId, path, keepUids)) |
| 109 | ✗ | return false; | |
| 110 |
2/2✓ Branch 121 → 81 taken 261 times.
✓ Branch 121 → 122 taken 87 times.
|
348 | for (const auto *table : {"events", "tasks", "calendar_resources"}) { |
| 111 |
1/1✓ Branch 81 → 82 taken 261 times.
|
261 | QSqlQuery prune(m_db); |
| 112 |
1/1✓ Branch 88 → 89 taken 261 times.
|
783 | prune.prepare(QStringLiteral("DELETE FROM %1 WHERE calendar_id = ? AND NOT EXISTS " |
| 113 | "(SELECT 1 FROM calendar_enum_seen seen WHERE seen.calendar_id = %1.calendar_id " | ||
| 114 | "AND seen.uid = %1.uid)") | ||
| 115 |
1/1✓ Branch 87 → 88 taken 261 times.
|
783 | .arg(QLatin1String(table))); |
| 116 |
1/1✓ Branch 94 → 95 taken 261 times.
|
261 | prune.addBindValue(calId); |
| 117 |
2/3✓ Branch 96 → 97 taken 261 times.
✗ Branch 97 → 98 not taken.
✓ Branch 97 → 115 taken 261 times.
|
261 | if (!prune.exec()) { |
| 118 | ✗ | qCWarning(lcCalStore) << "finishEnumeration prune" << table << prune.lastError().text(); | |
| 119 | ✗ | return false; | |
| 120 | } | ||
| 121 | 261 | } | |
| 122 | } else { | ||
| 123 |
5/5✓ Branch 123 → 124 taken 2 times.
✓ Branch 127 → 128 taken 2 times.
✓ Branch 128 → 129 taken 2 times.
✓ Branch 132 → 125 taken 2 times.
✓ Branch 132 → 133 taken 2 times.
|
4 | qCWarning(lcCalStore) << "finishEnumeration: nothing seen — skipping prune"; |
| 124 | } | ||
| 125 |
1/1✓ Branch 134 → 135 taken 108 times.
|
108 | return abandonEnumeration(accountId, path); |
| 126 | 108 | } | |
| 127 | |||
| 128 | 114 | bool CalendarStore::abandonEnumeration(const QString &accountId, const QString &path) { | |
| 129 |
1/1✓ Branch 2 → 3 taken 114 times.
|
114 | const qint64 calId = calendarIdForPath(accountId, path); |
| 130 |
2/2✓ Branch 3 → 4 taken 2 times.
✓ Branch 3 → 5 taken 112 times.
|
114 | if (calId < 0) |
| 131 | 2 | return false; | |
| 132 |
1/1✓ Branch 5 → 6 taken 112 times.
|
112 | QSqlQuery clear(m_db); |
| 133 |
1/1✓ Branch 9 → 10 taken 112 times.
|
112 | clear.prepare(QStringLiteral("DELETE FROM calendar_enum_seen WHERE calendar_id = ?")); |
| 134 |
1/1✓ Branch 14 → 15 taken 112 times.
|
112 | clear.addBindValue(calId); |
| 135 |
1/1✓ Branch 16 → 17 taken 112 times.
|
112 | QSqlQuery state(m_db); |
| 136 |
1/1✓ Branch 20 → 21 taken 112 times.
|
112 | state.prepare(QStringLiteral("UPDATE calendars SET enum_active = 0, enum_token = '' WHERE id = ?")); |
| 137 |
1/1✓ Branch 25 → 26 taken 112 times.
|
112 | state.addBindValue(calId); |
| 138 |
5/8✓ Branch 27 → 28 taken 112 times.
✓ Branch 28 → 29 taken 112 times.
✗ Branch 28 → 31 not taken.
✓ Branch 29 → 30 taken 112 times.
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 32 taken 112 times.
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 55 taken 112 times.
|
112 | if (!clear.exec() || !state.exec()) { |
| 139 | ✗ | qCWarning(lcCalStore) << "abandonEnumeration:" << clear.lastError().text() << state.lastError().text(); | |
| 140 | ✗ | return false; | |
| 141 | } | ||
| 142 | 112 | return true; | |
| 143 | 112 | } | |
| 144 |