data/DavPushStore.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "DavPushStore.h" | ||
| 2 | |||
| 3 | #include "DatabaseSecurity.h" | ||
| 4 | |||
| 5 | #include <QLoggingCategory> | ||
| 6 | #include <QRandomGenerator> | ||
| 7 | #include <QSqlError> | ||
| 8 | #include <QSqlQuery> | ||
| 9 | #include <QUuid> | ||
| 10 | |||
| 11 |
4/7✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 8 taken 1 time.
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 8 not taken.
✓ Branch 5 → 6 taken 1 time.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
|
2 | Q_LOGGING_CATEGORY(lcDavPushStore, "mailjd.davpush.store") |
| 12 | |||
| 13 | namespace { | ||
| 14 | |||
| 15 | 1848 | DavPushCollectionRecord collectionFrom(const QSqlQuery &q) { | |
| 16 | 1848 | DavPushCollectionRecord r; | |
| 17 |
2/2✓ Branch 2 → 3 taken 1848 times.
✓ Branch 3 → 4 taken 1848 times.
|
1848 | r.accountId = q.value(0).toString(); |
| 18 |
2/2✓ Branch 7 → 8 taken 1848 times.
✓ Branch 8 → 9 taken 1848 times.
|
1848 | r.path = q.value(1).toString(); |
| 19 |
2/2✓ Branch 12 → 13 taken 1848 times.
✓ Branch 13 → 14 taken 1848 times.
|
1848 | r.offer.capability = davPushCapabilityFromName(q.value(2).toString()); |
| 20 |
2/2✓ Branch 17 → 18 taken 1848 times.
✓ Branch 18 → 19 taken 1848 times.
|
1848 | r.offer.profile = static_cast<DavPushProfile>(q.value(3).toInt()); |
| 21 |
2/2✓ Branch 20 → 21 taken 1848 times.
✓ Branch 21 → 22 taken 1848 times.
|
1848 | r.offer.topic = q.value(4).toString(); |
| 22 |
2/2✓ Branch 25 → 26 taken 1848 times.
✓ Branch 26 → 27 taken 1848 times.
|
1848 | r.offer.vapidPublicKey = q.value(5).toByteArray(); |
| 23 |
2/2✓ Branch 30 → 31 taken 1848 times.
✓ Branch 31 → 32 taken 1848 times.
|
1848 | r.offer.reason = q.value(6).toString(); |
| 24 |
2/2✓ Branch 35 → 36 taken 1848 times.
✓ Branch 36 → 37 taken 1848 times.
|
1848 | r.checkedAt = q.value(7).toLongLong(); |
| 25 |
2/2✓ Branch 38 → 39 taken 1848 times.
✓ Branch 39 → 40 taken 1848 times.
|
1848 | r.registrationUrl = q.value(8).toString(); |
| 26 |
2/2✓ Branch 43 → 44 taken 1848 times.
✓ Branch 44 → 45 taken 1848 times.
|
1848 | r.registrationReceiver = q.value(9).toString(); |
| 27 |
2/2✓ Branch 48 → 49 taken 1848 times.
✓ Branch 49 → 50 taken 1848 times.
|
1848 | r.expiresAt = q.value(10).toLongLong(); |
| 28 |
2/2✓ Branch 51 → 52 taken 1848 times.
✓ Branch 52 → 53 taken 1848 times.
|
1848 | r.registeredAt = q.value(11).toLongLong(); |
| 29 |
2/2✓ Branch 54 → 55 taken 1848 times.
✓ Branch 55 → 56 taken 1848 times.
|
1848 | r.renewedAt = q.value(12).toLongLong(); |
| 30 |
2/2✓ Branch 57 → 58 taken 1848 times.
✓ Branch 58 → 59 taken 1848 times.
|
1848 | r.lastError = q.value(13).toString(); |
| 31 |
2/2✓ Branch 62 → 63 taken 1848 times.
✓ Branch 63 → 64 taken 1848 times.
|
1848 | r.lastErrorAt = q.value(14).toLongLong(); |
| 32 |
2/2✓ Branch 65 → 66 taken 1848 times.
✓ Branch 66 → 67 taken 1848 times.
|
1848 | r.nextRetryAt = q.value(15).toLongLong(); |
| 33 |
2/2✓ Branch 68 → 69 taken 1848 times.
✓ Branch 69 → 70 taken 1848 times.
|
1848 | r.failures = q.value(16).toInt(); |
| 34 |
2/2✓ Branch 71 → 72 taken 1848 times.
✓ Branch 72 → 73 taken 1848 times.
|
1848 | r.lastPushAt = q.value(17).toLongLong(); |
| 35 |
2/2✓ Branch 74 → 75 taken 1848 times.
✓ Branch 75 → 76 taken 1848 times.
|
1848 | r.lastSyncAt = q.value(18).toLongLong(); |
| 36 |
2/2✓ Branch 77 → 78 taken 1848 times.
✓ Branch 78 → 79 taken 1848 times.
|
1848 | r.registrationEndpoint = q.value(19).toString(); |
| 37 | 1848 | return r; | |
| 38 | ✗ | } | |
| 39 | |||
| 40 | const char *kCollectionColumns = | ||
| 41 | "account_id, path, capability, profile, topic, vapid_key, reason, checked_at, registration_url, " | ||
| 42 | "registration_receiver, expires_at, registered_at, renewed_at, last_error, last_error_at, next_retry_at, " | ||
| 43 | "failures, last_push_at, last_sync_at, endpoint_hash"; | ||
| 44 | |||
| 45 | 344 | qint64 hourOf(qint64 epochSeconds) { return epochSeconds - epochSeconds % 3600; } | |
| 46 | |||
| 47 | } // namespace | ||
| 48 | |||
| 49 | 868 | DavPushStore::~DavPushStore() { | |
| 50 |
2/2✓ Branch 3 → 4 taken 867 times.
✓ Branch 3 → 9 taken 1 time.
|
868 | if (m_db.isValid()) { |
| 51 | 867 | m_db.close(); | |
| 52 | 867 | m_db = QSqlDatabase(); | |
| 53 | 867 | QSqlDatabase::removeDatabase(m_connectionName); | |
| 54 | } | ||
| 55 | 868 | } | |
| 56 | |||
| 57 | 13653 | bool DavPushStore::exec(const QString &sql, const QVariantList &values) const { | |
| 58 |
1/1✓ Branch 2 → 3 taken 13653 times.
|
13653 | QSqlQuery q(m_db); |
| 59 |
1/1✓ Branch 3 → 4 taken 13653 times.
|
13653 | q.prepare(sql); |
| 60 |
3/3✓ Branch 8 → 9 taken 15356 times.
✓ Branch 11 → 6 taken 15356 times.
✓ Branch 11 → 12 taken 13653 times.
|
29009 | for (const auto &value : values) q.addBindValue(value); |
| 61 |
2/3✓ Branch 12 → 13 taken 13653 times.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 34 taken 13653 times.
|
13653 | if (!q.exec()) { |
| 62 | ✗ | m_lastError = q.lastError().text(); | |
| 63 | ✗ | qCWarning(lcDavPushStore) << "SQL failed:" << m_lastError << sql.left(80); | |
| 64 | ✗ | return false; | |
| 65 | } | ||
| 66 | 13653 | return true; | |
| 67 | 13653 | } | |
| 68 | |||
| 69 | 868 | bool DavPushStore::open(const QString &path) { | |
| 70 |
3/3✓ Branch 2 → 3 taken 868 times.
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 11 taken 867 times.
|
868 | if (!DatabaseSecurity::preparePath(path)) { |
| 71 | 1 | m_lastError = QStringLiteral("cannot prepare a private database path"); | |
| 72 | 1 | return false; | |
| 73 | } | ||
| 74 |
4/4✓ Branch 11 → 12 taken 867 times.
✓ Branch 12 → 13 taken 867 times.
✓ Branch 13 → 14 taken 867 times.
✓ Branch 17 → 18 taken 867 times.
|
1734 | m_connectionName = QStringLiteral("davpush_") + QUuid::createUuid().toString(QUuid::WithoutBraces).left(8); |
| 75 |
2/2✓ Branch 27 → 28 taken 867 times.
✓ Branch 28 → 29 taken 867 times.
|
1734 | m_db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), m_connectionName); |
| 76 |
1/1✓ Branch 32 → 33 taken 867 times.
|
867 | m_db.setDatabaseName(path); |
| 77 |
5/8✓ Branch 33 → 34 taken 867 times.
✓ Branch 34 → 35 taken 867 times.
✗ Branch 34 → 37 not taken.
✓ Branch 35 → 36 taken 867 times.
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 38 taken 867 times.
✗ Branch 39 → 40 not taken.
✓ Branch 39 → 58 taken 867 times.
|
867 | if (!m_db.open() || !DatabaseSecurity::restrictExistingFile(path)) { |
| 78 | ✗ | m_lastError = m_db.lastError().text(); | |
| 79 | ✗ | qCWarning(lcDavPushStore) << "open failed:" << m_lastError; | |
| 80 | ✗ | m_db.close(); | |
| 81 | ✗ | return false; | |
| 82 | } | ||
| 83 |
1/1✓ Branch 58 → 59 taken 867 times.
|
867 | QSqlQuery pragma(m_db); |
| 84 |
1/1✓ Branch 62 → 63 taken 867 times.
|
867 | pragma.exec(QStringLiteral("PRAGMA journal_mode=WAL")); |
| 85 |
1/1✓ Branch 68 → 69 taken 867 times.
|
867 | pragma.exec(QStringLiteral("PRAGMA busy_timeout=5000")); |
| 86 |
3/3✓ Branch 71 → 72 taken 867 times.
✓ Branch 72 → 73 taken 1 time.
✓ Branch 72 → 75 taken 866 times.
|
867 | if (!createSchema()) { |
| 87 |
1/1✓ Branch 73 → 74 taken 1 time.
|
1 | m_db.close(); |
| 88 | 1 | return false; | |
| 89 | } | ||
| 90 | 866 | return true; | |
| 91 | 867 | } | |
| 92 | |||
| 93 | 867 | bool DavPushStore::createSchema() { | |
| 94 |
1/1✓ Branch 2 → 3 taken 867 times.
|
867 | QSqlQuery version(m_db); |
| 95 |
7/16✓ Branch 6 → 7 taken 867 times.
✓ Branch 7 → 8 taken 867 times.
✗ Branch 7 → 10 not taken.
✓ Branch 8 → 9 taken 867 times.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 867 times.
✓ Branch 12 → 13 taken 867 times.
✗ Branch 12 → 14 not taken.
✓ Branch 14 → 15 taken 867 times.
✗ Branch 14 → 16 not taken.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 867 times.
✗ Branch 170 → 171 not taken.
✗ Branch 170 → 172 not taken.
✗ Branch 174 → 175 not taken.
✗ Branch 174 → 176 not taken.
|
1734 | if (!version.exec(QStringLiteral("PRAGMA user_version")) || !version.next()) return false; |
| 96 |
2/2✓ Branch 18 → 19 taken 867 times.
✓ Branch 19 → 20 taken 867 times.
|
867 | const int current = version.value(0).toInt(); |
| 97 |
2/2✓ Branch 21 → 22 taken 1 time.
✓ Branch 21 → 42 taken 866 times.
|
867 | if (current > SchemaVersion) { |
| 98 | // A newer MailJD wrote this file: push stays off instead of guessing. | ||
| 99 |
1/1✓ Branch 26 → 27 taken 1 time.
|
2 | m_lastError = QStringLiteral("dav-push.sqlite has the newer schema %1").arg(current); |
| 100 |
5/5✓ Branch 31 → 32 taken 1 time.
✓ Branch 35 → 36 taken 1 time.
✓ Branch 36 → 37 taken 1 time.
✓ Branch 40 → 33 taken 1 time.
✓ Branch 40 → 41 taken 1 time.
|
2 | qCWarning(lcDavPushStore) << m_lastError; |
| 101 | 1 | return false; | |
| 102 | } | ||
| 103 |
2/2✓ Branch 42 → 43 taken 18 times.
✓ Branch 42 → 44 taken 848 times.
|
866 | if (current == SchemaVersion) return true; |
| 104 |
2/2✓ Branch 44 → 45 taken 1 time.
✓ Branch 44 → 94 taken 847 times.
|
848 | if (current == 1) { |
| 105 | // v1 -> v2: the registered endpoint's hash (unknown for old rows). | ||
| 106 |
2/3✓ Branch 45 → 46 taken 1 time.
✗ Branch 46 → 47 not taken.
✓ Branch 46 → 48 taken 1 time.
|
1 | if (!m_db.transaction()) return false; |
| 107 |
5/15✓ Branch 52 → 53 taken 1 time.
✓ Branch 53 → 54 taken 1 time.
✗ Branch 53 → 62 not taken.
✓ Branch 74 → 75 taken 1 time.
✗ Branch 74 → 76 not taken.
✓ Branch 76 → 77 taken 1 time.
✗ Branch 76 → 78 not taken.
✗ Branch 78 → 79 not taken.
✓ Branch 78 → 81 taken 1 time.
✗ Branch 211 → 212 not taken.
✗ Branch 211 → 213 not taken.
✗ Branch 215 → 216 not taken.
✗ Branch 215 → 217 not taken.
✗ Branch 219 → 220 not taken.
✗ Branch 219 → 221 not taken.
|
5 | if (!exec(QStringLiteral("ALTER TABLE collections ADD COLUMN endpoint_hash TEXT DEFAULT ''")) || |
| 108 |
8/22✓ Branch 59 → 60 taken 1 time.
✓ Branch 60 → 61 taken 1 time.
✗ Branch 61 → 62 not taken.
✓ Branch 61 → 63 taken 1 time.
✓ Branch 64 → 65 taken 1 time.
✗ Branch 64 → 66 not taken.
✓ Branch 66 → 67 taken 1 time.
✗ Branch 66 → 68 not taken.
✓ Branch 68 → 69 taken 1 time.
✗ Branch 68 → 70 not taken.
✓ Branch 70 → 71 taken 1 time.
✗ Branch 70 → 72 not taken.
✓ Branch 72 → 73 taken 1 time.
✗ Branch 72 → 74 not taken.
✗ Branch 194 → 195 not taken.
✗ Branch 194 → 196 not taken.
✗ Branch 199 → 200 not taken.
✗ Branch 199 → 201 not taken.
✗ Branch 203 → 204 not taken.
✗ Branch 203 → 205 not taken.
✗ Branch 207 → 208 not taken.
✗ Branch 207 → 209 not taken.
|
3 | !exec(QStringLiteral("PRAGMA user_version = %1").arg(SchemaVersion))) { |
| 109 | ✗ | m_db.rollback(); | |
| 110 | ✗ | return false; | |
| 111 | } | ||
| 112 |
6/6✓ Branch 81 → 82 taken 1 time.
✓ Branch 85 → 86 taken 1 time.
✓ Branch 86 → 87 taken 1 time.
✓ Branch 87 → 88 taken 1 time.
✓ Branch 91 → 83 taken 1 time.
✓ Branch 91 → 92 taken 1 time.
|
2 | qCInfo(lcDavPushStore) << "dav-push.sqlite migrated to schema" << SchemaVersion; |
| 113 |
1/1✓ Branch 92 → 93 taken 1 time.
|
1 | return m_db.commit(); |
| 114 | } | ||
| 115 | const QStringList statements{ | ||
| 116 | ✗ | QStringLiteral("CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT)"), | |
| 117 | 847 | QStringLiteral("CREATE TABLE IF NOT EXISTS collections (account_id TEXT NOT NULL, path TEXT NOT NULL, " | |
| 118 | "capability TEXT DEFAULT 'unknown', profile INTEGER DEFAULT 0, topic TEXT DEFAULT '', " | ||
| 119 | "vapid_key BLOB, reason TEXT DEFAULT '', checked_at INTEGER DEFAULT 0, " | ||
| 120 | "registration_url TEXT DEFAULT '', registration_receiver TEXT DEFAULT '', " | ||
| 121 | "expires_at INTEGER DEFAULT 0, registered_at INTEGER DEFAULT 0, renewed_at INTEGER DEFAULT 0, " | ||
| 122 | "last_error TEXT DEFAULT '', last_error_at INTEGER DEFAULT 0, next_retry_at INTEGER DEFAULT 0, " | ||
| 123 | "failures INTEGER DEFAULT 0, last_push_at INTEGER DEFAULT 0, last_sync_at INTEGER DEFAULT 0, " | ||
| 124 | "endpoint_hash TEXT DEFAULT '', PRIMARY KEY(account_id, path))"), | ||
| 125 | 847 | QStringLiteral("CREATE TABLE IF NOT EXISTS receivers (account_id TEXT PRIMARY KEY, receiver_id TEXT NOT NULL, " | |
| 126 | "provider TEXT NOT NULL, server TEXT NOT NULL, created_at INTEGER DEFAULT 0, " | ||
| 127 | "generation INTEGER DEFAULT 0)"), | ||
| 128 | 847 | QStringLiteral("CREATE TABLE IF NOT EXISTS cleanup (id INTEGER PRIMARY KEY, account_id TEXT NOT NULL, " | |
| 129 | "registration_url TEXT NOT NULL, created_at INTEGER DEFAULT 0, attempts INTEGER DEFAULT 0)"), | ||
| 130 | 847 | QStringLiteral("CREATE TABLE IF NOT EXISTS stats_counters (scope TEXT NOT NULL, name TEXT NOT NULL, " | |
| 131 | "value INTEGER DEFAULT 0, PRIMARY KEY(scope, name))"), | ||
| 132 | 847 | QStringLiteral("CREATE TABLE IF NOT EXISTS stats_hourly (scope TEXT NOT NULL, hour INTEGER NOT NULL, " | |
| 133 | "name TEXT NOT NULL, value INTEGER DEFAULT 0, PRIMARY KEY(scope, hour, name))"), | ||
| 134 | 847 | QStringLiteral("CREATE TABLE IF NOT EXISTS stats_latency (id INTEGER PRIMARY KEY, scope TEXT NOT NULL, " | |
| 135 | "kind TEXT NOT NULL, millis INTEGER NOT NULL, at INTEGER NOT NULL)"), | ||
| 136 | 847 | QStringLiteral("CREATE TABLE IF NOT EXISTS stats_events (id INTEGER PRIMARY KEY, scope TEXT NOT NULL, " | |
| 137 | "at INTEGER NOT NULL, kind TEXT NOT NULL, detail TEXT DEFAULT '')"), | ||
| 138 | 847 | QStringLiteral("CREATE TABLE IF NOT EXISTS stats_scopes (scope TEXT PRIMARY KEY, since INTEGER DEFAULT 0)"), | |
| 139 | 847 | QStringLiteral("CREATE INDEX IF NOT EXISTS idx_latency_scope ON stats_latency(scope, kind, id)"), | |
| 140 | 847 | QStringLiteral("CREATE INDEX IF NOT EXISTS idx_events_scope ON stats_events(scope, id)"), | |
| 141 |
2/4✓ Branch 134 → 135 taken 10164 times.
✓ Branch 134 → 136 taken 847 times.
✗ Branch 229 → 230 not taken.
✗ Branch 229 → 231 not taken.
|
12705 | QStringLiteral("PRAGMA user_version = %1").arg(SchemaVersion)}; |
| 142 |
2/3✓ Branch 149 → 150 taken 847 times.
✗ Branch 150 → 151 not taken.
✓ Branch 150 → 152 taken 847 times.
|
847 | if (!m_db.transaction()) return false; |
| 143 |
2/2✓ Branch 163 → 154 taken 10164 times.
✓ Branch 163 → 164 taken 847 times.
|
11011 | for (const QString &statement : statements) { |
| 144 |
2/3✓ Branch 156 → 157 taken 10164 times.
✗ Branch 158 → 159 not taken.
✓ Branch 158 → 161 taken 10164 times.
|
10164 | if (!exec(statement)) { |
| 145 | ✗ | m_db.rollback(); | |
| 146 | ✗ | return false; | |
| 147 | } | ||
| 148 | } | ||
| 149 |
1/1✓ Branch 164 → 165 taken 847 times.
|
847 | return m_db.commit(); |
| 150 | 12725 | } | |
| 151 | |||
| 152 | 861 | QString DavPushStore::installationId() { | |
| 153 |
1/1✓ Branch 2 → 3 taken 861 times.
|
861 | QSqlQuery q(m_db); |
| 154 |
1/1✓ Branch 6 → 7 taken 861 times.
|
861 | q.prepare(QStringLiteral("SELECT value FROM meta WHERE key = 'installation_id'")); |
| 155 |
16/22✓ Branch 9 → 10 taken 861 times.
✓ Branch 10 → 11 taken 861 times.
✗ Branch 10 → 18 not taken.
✓ Branch 11 → 12 taken 861 times.
✓ Branch 12 → 13 taken 17 times.
✓ Branch 12 → 18 taken 844 times.
✓ Branch 13 → 14 taken 17 times.
✓ Branch 14 → 15 taken 17 times.
✓ Branch 16 → 17 taken 17 times.
✗ Branch 16 → 18 not taken.
✓ Branch 19 → 20 taken 17 times.
✓ Branch 19 → 21 taken 844 times.
✓ Branch 21 → 22 taken 17 times.
✓ Branch 21 → 23 taken 844 times.
✓ Branch 23 → 24 taken 17 times.
✓ Branch 23 → 29 taken 844 times.
✓ Branch 24 → 25 taken 17 times.
✓ Branch 25 → 26 taken 17 times.
✗ Branch 54 → 55 not taken.
✗ Branch 54 → 56 not taken.
✗ Branch 58 → 59 not taken.
✗ Branch 58 → 60 not taken.
|
861 | if (q.exec() && q.next() && !q.value(0).toString().isEmpty()) return q.value(0).toString(); |
| 156 |
2/2✓ Branch 29 → 30 taken 844 times.
✓ Branch 30 → 31 taken 844 times.
|
844 | const QString id = QUuid::createUuid().toString(QUuid::WithoutBraces); |
| 157 |
3/5✓ Branch 36 → 37 taken 844 times.
✓ Branch 41 → 42 taken 844 times.
✓ Branch 41 → 43 taken 844 times.
✗ Branch 75 → 76 not taken.
✗ Branch 75 → 77 not taken.
|
1688 | exec(QStringLiteral("INSERT OR REPLACE INTO meta (key, value) VALUES ('installation_id', ?)"), {id}); |
| 158 | 844 | return id; | |
| 159 | 1705 | } | |
| 160 | |||
| 161 | 1741 | std::optional<DavPushCollectionRecord> DavPushStore::collection(const QString &accountId, const QString &path) const { | |
| 162 |
1/1✓ Branch 2 → 3 taken 1741 times.
|
1741 | QSqlQuery q(m_db); |
| 163 |
1/1✓ Branch 9 → 10 taken 1741 times.
|
5223 | q.prepare(QStringLiteral("SELECT %1 FROM collections WHERE account_id = ? AND path = ?") |
| 164 |
1/1✓ Branch 8 → 9 taken 1741 times.
|
5223 | .arg(QLatin1String(kCollectionColumns))); |
| 165 |
1/1✓ Branch 15 → 16 taken 1741 times.
|
1741 | q.addBindValue(accountId); |
| 166 |
1/1✓ Branch 19 → 20 taken 1741 times.
|
1741 | q.addBindValue(path); |
| 167 |
7/8✓ Branch 21 → 22 taken 1741 times.
✓ Branch 22 → 23 taken 1741 times.
✗ Branch 22 → 25 not taken.
✓ Branch 23 → 24 taken 1741 times.
✓ Branch 24 → 25 taken 378 times.
✓ Branch 24 → 26 taken 1363 times.
✓ Branch 27 → 28 taken 378 times.
✓ Branch 27 → 29 taken 1363 times.
|
1741 | if (!q.exec() || !q.next()) return std::nullopt; |
| 168 |
1/1✓ Branch 29 → 30 taken 1363 times.
|
1363 | return collectionFrom(q); |
| 169 | 1741 | } | |
| 170 | |||
| 171 | 289 | QList<DavPushCollectionRecord> DavPushStore::collections(const QString &accountId) const { | |
| 172 | 289 | QList<DavPushCollectionRecord> result; | |
| 173 |
1/1✓ Branch 2 → 3 taken 289 times.
|
289 | QSqlQuery q(m_db); |
| 174 |
1/1✓ Branch 9 → 10 taken 289 times.
|
867 | q.prepare(QStringLiteral("SELECT %1 FROM collections WHERE ? = '' OR account_id = ? ORDER BY account_id, path") |
| 175 |
1/1✓ Branch 8 → 9 taken 289 times.
|
578 | .arg(QLatin1String(kCollectionColumns))); |
| 176 | // A null QString binds as SQL NULL; "" means "all accounts". | ||
| 177 |
4/4✓ Branch 14 → 15 taken 2 times.
✓ Branch 14 → 18 taken 287 times.
✓ Branch 19 → 20 taken 2 times.
✓ Branch 19 → 21 taken 287 times.
|
291 | const QString filter = accountId.isEmpty() ? QStringLiteral("") : accountId; |
| 178 |
1/1✓ Branch 23 → 24 taken 289 times.
|
289 | q.addBindValue(filter); |
| 179 |
1/1✓ Branch 27 → 28 taken 289 times.
|
289 | q.addBindValue(filter); |
| 180 |
2/3✓ Branch 29 → 30 taken 289 times.
✓ Branch 30 → 31 taken 289 times.
✗ Branch 30 → 38 not taken.
|
289 | if (q.exec()) |
| 181 |
5/5✓ Branch 32 → 33 taken 485 times.
✓ Branch 33 → 34 taken 485 times.
✓ Branch 36 → 37 taken 774 times.
✓ Branch 37 → 32 taken 485 times.
✓ Branch 37 → 38 taken 289 times.
|
774 | while (q.next()) result.append(collectionFrom(q)); |
| 182 | 289 | return result; | |
| 183 | 289 | } | |
| 184 | |||
| 185 | 319 | bool DavPushStore::saveCollection(const DavPushCollectionRecord &r) { | |
| 186 |
3/5✓ Branch 30 → 31 taken 319 times.
✓ Branch 37 → 38 taken 6380 times.
✓ Branch 37 → 39 taken 319 times.
✗ Branch 56 → 57 not taken.
✗ Branch 56 → 58 not taken.
|
7656 | return exec(QStringLiteral("INSERT OR REPLACE INTO collections (%1) VALUES " |
| 187 | "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") | ||
| 188 |
1/1✓ Branch 29 → 30 taken 319 times.
|
957 | .arg(QLatin1String(kCollectionColumns)), |
| 189 | 319 | {r.accountId, r.path, davPushCapabilityName(r.offer.capability), int(r.offer.profile), r.offer.topic, | |
| 190 | 1595 | r.offer.vapidPublicKey, r.offer.reason, r.checkedAt, r.registrationUrl, r.registrationReceiver, | |
| 191 | 2233 | r.expiresAt, r.registeredAt, r.renewedAt, r.lastError, r.lastErrorAt, r.nextRetryAt, r.failures, | |
| 192 | 638 | r.lastPushAt, r.lastSyncAt, r.registrationEndpoint}); | |
| 193 | 319 | } | |
| 194 | |||
| 195 | 4 | bool DavPushStore::removeCollection(const QString &accountId, const QString &path) { | |
| 196 |
3/5✓ Branch 8 → 9 taken 4 times.
✓ Branch 14 → 15 taken 8 times.
✓ Branch 14 → 16 taken 4 times.
✗ Branch 27 → 28 not taken.
✗ Branch 27 → 29 not taken.
|
12 | return exec(QStringLiteral("DELETE FROM collections WHERE account_id = ? AND path = ?"), {accountId, path}); |
| 197 | 4 | } | |
| 198 | |||
| 199 | 103 | std::optional<DavPushReceiverRecord> DavPushStore::receiver(const QString &accountId) const { | |
| 200 |
1/1✓ Branch 2 → 3 taken 103 times.
|
103 | QSqlQuery q(m_db); |
| 201 |
1/1✓ Branch 6 → 7 taken 103 times.
|
103 | q.prepare(QStringLiteral("SELECT account_id, receiver_id, provider, server, created_at, generation " |
| 202 | "FROM receivers WHERE account_id = ?")); | ||
| 203 |
1/1✓ Branch 11 → 12 taken 103 times.
|
103 | q.addBindValue(accountId); |
| 204 |
7/8✓ Branch 13 → 14 taken 103 times.
✓ Branch 14 → 15 taken 103 times.
✗ Branch 14 → 17 not taken.
✓ Branch 15 → 16 taken 103 times.
✓ Branch 16 → 17 taken 99 times.
✓ Branch 16 → 18 taken 4 times.
✓ Branch 19 → 20 taken 99 times.
✓ Branch 19 → 21 taken 4 times.
|
103 | if (!q.exec() || !q.next()) return std::nullopt; |
| 205 |
7/15✓ Branch 21 → 22 taken 4 times.
✓ Branch 22 → 23 taken 4 times.
✓ Branch 23 → 24 taken 4 times.
✓ Branch 24 → 25 taken 4 times.
✓ Branch 25 → 26 taken 4 times.
✓ Branch 26 → 27 taken 4 times.
✓ Branch 28 → 29 taken 4 times.
✗ Branch 61 → 62 not taken.
✗ Branch 61 → 63 not taken.
✗ Branch 67 → 68 not taken.
✗ Branch 67 → 69 not taken.
✗ Branch 73 → 74 not taken.
✗ Branch 73 → 75 not taken.
✗ Branch 79 → 80 not taken.
✗ Branch 79 → 81 not taken.
|
16 | return DavPushReceiverRecord{q.value(0).toString(), q.value(1).toString(), q.value(2).toString(), |
| 206 |
5/5✓ Branch 27 → 28 taken 4 times.
✓ Branch 29 → 30 taken 4 times.
✓ Branch 30 → 31 taken 4 times.
✓ Branch 31 → 32 taken 4 times.
✓ Branch 32 → 33 taken 4 times.
|
12 | q.value(3).toString(), q.value(4).toLongLong(), q.value(5).toInt()}; |
| 207 | 103 | } | |
| 208 | |||
| 209 | 34 | bool DavPushStore::saveReceiver(const DavPushReceiverRecord &r) { | |
| 210 |
3/5✓ Branch 12 → 13 taken 34 times.
✓ Branch 18 → 19 taken 204 times.
✓ Branch 18 → 20 taken 34 times.
✗ Branch 31 → 32 not taken.
✗ Branch 31 → 33 not taken.
|
272 | return exec(QStringLiteral("INSERT OR REPLACE INTO receivers (account_id, receiver_id, provider, server, " |
| 211 | "created_at, generation) VALUES (?, ?, ?, ?, ?, ?)"), | ||
| 212 | 68 | {r.accountId, r.receiverId, r.provider, r.server, r.createdAt, r.generation}); | |
| 213 | 34 | } | |
| 214 | |||
| 215 | 1 | bool DavPushStore::removeReceiver(const QString &accountId) { | |
| 216 |
3/5✓ Branch 7 → 8 taken 1 time.
✓ Branch 13 → 14 taken 1 time.
✓ Branch 13 → 15 taken 1 time.
✗ Branch 26 → 27 not taken.
✗ Branch 26 → 28 not taken.
|
2 | return exec(QStringLiteral("DELETE FROM receivers WHERE account_id = ?"), {accountId}); |
| 217 | 1 | } | |
| 218 | |||
| 219 | ✗ | QStringList DavPushStore::receiverAccounts() const { | |
| 220 | ✗ | QStringList accounts; | |
| 221 | ✗ | QSqlQuery q(m_db); | |
| 222 | ✗ | if (q.exec(QStringLiteral("SELECT account_id FROM receivers ORDER BY account_id"))) | |
| 223 | ✗ | while (q.next()) accounts << q.value(0).toString(); | |
| 224 | ✗ | return accounts; | |
| 225 | ✗ | } | |
| 226 | |||
| 227 | 217 | bool DavPushStore::queueCleanup(const QString &accountId, const QString ®istrationUrl, qint64 now) { | |
| 228 |
1/2✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 217 times.
|
217 | if (registrationUrl.isEmpty()) return true; |
| 229 |
1/1✓ Branch 5 → 6 taken 217 times.
|
217 | QSqlQuery count(m_db); |
| 230 |
7/14✓ Branch 9 → 10 taken 217 times.
✓ Branch 10 → 11 taken 217 times.
✗ Branch 10 → 17 not taken.
✓ Branch 11 → 12 taken 217 times.
✓ Branch 12 → 13 taken 217 times.
✗ Branch 12 → 17 not taken.
✓ Branch 22 → 23 taken 217 times.
✗ Branch 22 → 24 not taken.
✓ Branch 24 → 25 taken 5 times.
✓ Branch 24 → 34 taken 212 times.
✗ Branch 56 → 57 not taken.
✗ Branch 56 → 58 not taken.
✗ Branch 60 → 61 not taken.
✗ Branch 60 → 62 not taken.
|
868 | if (count.exec(QStringLiteral("SELECT COUNT(*) FROM cleanup")) && count.next() && |
| 231 |
6/10✓ Branch 13 → 14 taken 217 times.
✓ Branch 14 → 15 taken 217 times.
✓ Branch 15 → 16 taken 5 times.
✓ Branch 15 → 17 taken 212 times.
✓ Branch 18 → 19 taken 217 times.
✗ Branch 18 → 20 not taken.
✓ Branch 20 → 21 taken 217 times.
✗ Branch 20 → 22 not taken.
✗ Branch 52 → 53 not taken.
✗ Branch 52 → 54 not taken.
|
434 | count.value(0).toInt() >= kMaxCleanupEntries) { |
| 232 | // Bounded: the oldest entry goes; the server expiry ends it anyway. | ||
| 233 |
1/1✓ Branch 29 → 30 taken 5 times.
|
10 | exec(QStringLiteral("DELETE FROM cleanup WHERE id = (SELECT MIN(id) FROM cleanup)")); |
| 234 | } | ||
| 235 |
3/5✓ Branch 41 → 42 taken 217 times.
✓ Branch 47 → 48 taken 651 times.
✓ Branch 47 → 49 taken 217 times.
✗ Branch 82 → 83 not taken.
✗ Branch 82 → 84 not taken.
|
1085 | return exec(QStringLiteral("INSERT INTO cleanup (account_id, registration_url, created_at) VALUES (?, ?, ?)"), |
| 236 | 217 | {accountId, registrationUrl, now}); | |
| 237 | 434 | } | |
| 238 | |||
| 239 | 88 | QList<DavPushCleanupEntry> DavPushStore::cleanupEntries(const QString &accountId) const { | |
| 240 | 88 | QList<DavPushCleanupEntry> result; | |
| 241 |
1/1✓ Branch 2 → 3 taken 88 times.
|
88 | QSqlQuery q(m_db); |
| 242 |
1/1✓ Branch 6 → 7 taken 88 times.
|
88 | q.prepare(QStringLiteral("SELECT id, account_id, registration_url, created_at, attempts FROM cleanup " |
| 243 | "WHERE ? = '' OR account_id = ? ORDER BY id")); | ||
| 244 | // A null QString binds as SQL NULL; "" means "all accounts". | ||
| 245 |
4/4✓ Branch 10 → 11 taken 5 times.
✓ Branch 10 → 14 taken 83 times.
✓ Branch 15 → 16 taken 5 times.
✓ Branch 15 → 17 taken 83 times.
|
93 | const QString filter = accountId.isEmpty() ? QStringLiteral("") : accountId; |
| 246 |
1/1✓ Branch 19 → 20 taken 88 times.
|
88 | q.addBindValue(filter); |
| 247 |
1/1✓ Branch 23 → 24 taken 88 times.
|
88 | q.addBindValue(filter); |
| 248 |
2/3✓ Branch 25 → 26 taken 88 times.
✓ Branch 26 → 27 taken 88 times.
✗ Branch 26 → 48 not taken.
|
88 | if (q.exec()) |
| 249 |
3/3✓ Branch 46 → 47 taken 916 times.
✓ Branch 47 → 28 taken 828 times.
✓ Branch 47 → 48 taken 88 times.
|
916 | while (q.next()) |
| 250 |
3/3✓ Branch 28 → 29 taken 828 times.
✓ Branch 29 → 30 taken 828 times.
✓ Branch 35 → 36 taken 828 times.
|
3312 | result.append({q.value(0).toLongLong(), q.value(1).toString(), q.value(2).toString(), q.value(3).toLongLong(), |
| 251 |
1/1✓ Branch 37 → 38 taken 828 times.
|
1656 | q.value(4).toInt()}); |
| 252 | 88 | return result; | |
| 253 | 2572 | } | |
| 254 | |||
| 255 | 10 | bool DavPushStore::updateCleanupAttempt(qint64 id) { | |
| 256 | // S106-078 (INV-12): the attempts are bounded — after kMaxCleanupAttempts | ||
| 257 | // failed DELETEs the entry goes (the server's Expires ends the | ||
| 258 | // subscription anyway); it used to be retried at every converge forever. | ||
| 259 |
7/21✓ Branch 7 → 8 taken 10 times.
✓ Branch 8 → 9 taken 10 times.
✗ Branch 8 → 18 not taken.
✓ Branch 31 → 32 taken 10 times.
✗ Branch 31 → 33 not taken.
✓ Branch 33 → 34 taken 10 times.
✗ Branch 33 → 35 not taken.
✓ Branch 35 → 36 taken 10 times.
✗ Branch 35 → 39 not taken.
✓ Branch 37 → 38 taken 10 times.
✓ Branch 37 → 39 taken 10 times.
✗ Branch 65 → 66 not taken.
✗ Branch 65 → 67 not taken.
✗ Branch 69 → 70 not taken.
✗ Branch 69 → 71 not taken.
✗ Branch 73 → 74 not taken.
✗ Branch 73 → 75 not taken.
✗ Branch 77 → 78 not taken.
✗ Branch 77 → 81 not taken.
✗ Branch 79 → 80 not taken.
✗ Branch 79 → 81 not taken.
|
60 | return exec(QStringLiteral("UPDATE cleanup SET attempts = attempts + 1 WHERE id = ?"), {id}) && |
| 260 |
9/25✓ Branch 15 → 16 taken 10 times.
✓ Branch 16 → 17 taken 10 times.
✗ Branch 16 → 18 not taken.
✓ Branch 19 → 20 taken 10 times.
✗ Branch 19 → 21 not taken.
✓ Branch 21 → 22 taken 10 times.
✗ Branch 21 → 23 not taken.
✓ Branch 23 → 24 taken 10 times.
✗ Branch 23 → 25 not taken.
✓ Branch 25 → 26 taken 10 times.
✗ Branch 25 → 29 not taken.
✓ Branch 27 → 28 taken 20 times.
✓ Branch 27 → 29 taken 10 times.
✓ Branch 29 → 30 taken 10 times.
✗ Branch 29 → 31 not taken.
✗ Branch 41 → 42 not taken.
✗ Branch 41 → 43 not taken.
✗ Branch 45 → 46 not taken.
✗ Branch 45 → 47 not taken.
✗ Branch 49 → 50 not taken.
✗ Branch 49 → 51 not taken.
✗ Branch 53 → 54 not taken.
✗ Branch 53 → 57 not taken.
✗ Branch 55 → 56 not taken.
✗ Branch 55 → 57 not taken.
|
50 | exec(QStringLiteral("DELETE FROM cleanup WHERE id = ? AND attempts >= ?"), {id, kMaxCleanupAttempts}); |
| 261 | 20 | } | |
| 262 | |||
| 263 | 12 | bool DavPushStore::removeCleanup(qint64 id) { | |
| 264 |
3/5✓ Branch 7 → 8 taken 12 times.
✓ Branch 13 → 14 taken 12 times.
✓ Branch 13 → 15 taken 12 times.
✗ Branch 26 → 27 not taken.
✗ Branch 26 → 28 not taken.
|
24 | return exec(QStringLiteral("DELETE FROM cleanup WHERE id = ?"), {id}); |
| 265 | 12 | } | |
| 266 | |||
| 267 | 2 | bool DavPushStore::removeCleanupForAccount(const QString &accountId) { | |
| 268 |
3/5✓ Branch 7 → 8 taken 2 times.
✓ Branch 13 → 14 taken 2 times.
✓ Branch 13 → 15 taken 2 times.
✗ Branch 26 → 27 not taken.
✗ Branch 26 → 28 not taken.
|
4 | return exec(QStringLiteral("DELETE FROM cleanup WHERE account_id = ?"), {accountId}); |
| 269 | 2 | } | |
| 270 | |||
| 271 | 35 | bool DavPushStore::beginTransaction() { return m_db.transaction(); } | |
| 272 | |||
| 273 | 35 | bool DavPushStore::commitTransaction() { return m_db.commit(); } | |
| 274 | |||
| 275 | ✗ | void DavPushStore::rollbackTransaction() { m_db.rollback(); } | |
| 276 | |||
| 277 | 271 | bool DavPushStore::addCounter(const QString &scope, const QString &name, qint64 delta, qint64 now) { | |
| 278 |
5/18✓ Branch 54 → 55 taken 271 times.
✗ Branch 54 → 56 not taken.
✓ Branch 56 → 57 taken 271 times.
✗ Branch 56 → 58 not taken.
✓ Branch 58 → 59 taken 271 times.
✗ Branch 58 → 62 not taken.
✓ Branch 60 → 61 taken 542 times.
✓ Branch 60 → 62 taken 271 times.
✗ Branch 112 → 113 not taken.
✗ Branch 112 → 114 not taken.
✗ Branch 116 → 117 not taken.
✗ Branch 116 → 118 not taken.
✗ Branch 120 → 121 not taken.
✗ Branch 120 → 122 not taken.
✗ Branch 124 → 125 not taken.
✗ Branch 124 → 128 not taken.
✗ Branch 126 → 127 not taken.
✗ Branch 126 → 128 not taken.
|
1084 | return exec(QStringLiteral("INSERT INTO stats_scopes (scope, since) VALUES (?, ?) ON CONFLICT(scope) DO NOTHING"), |
| 279 | 271 | {scope, now}) && | |
| 280 |
8/23✓ Branch 17 → 18 taken 271 times.
✓ Branch 18 → 19 taken 271 times.
✗ Branch 18 → 31 not taken.
✓ Branch 44 → 45 taken 271 times.
✗ Branch 44 → 46 not taken.
✓ Branch 46 → 47 taken 271 times.
✗ Branch 46 → 48 not taken.
✓ Branch 48 → 49 taken 271 times.
✗ Branch 48 → 52 not taken.
✓ Branch 50 → 51 taken 813 times.
✓ Branch 50 → 52 taken 271 times.
✓ Branch 52 → 53 taken 271 times.
✗ Branch 52 → 54 not taken.
✗ Branch 88 → 89 not taken.
✗ Branch 88 → 90 not taken.
✗ Branch 92 → 93 not taken.
✗ Branch 92 → 94 not taken.
✗ Branch 96 → 97 not taken.
✗ Branch 96 → 98 not taken.
✗ Branch 100 → 101 not taken.
✗ Branch 100 → 104 not taken.
✗ Branch 102 → 103 not taken.
✗ Branch 102 → 104 not taken.
|
1355 | exec(QStringLiteral("INSERT INTO stats_counters (scope, name, value) VALUES (?, ?, ?) " |
| 281 | "ON CONFLICT(scope, name) DO UPDATE SET value = value + excluded.value"), | ||
| 282 |
2/3✓ Branch 8 → 9 taken 271 times.
✓ Branch 9 → 10 taken 271 times.
✗ Branch 9 → 31 not taken.
|
1084 | {scope, name, delta}) && |
| 283 |
8/23✓ Branch 28 → 29 taken 271 times.
✓ Branch 29 → 30 taken 271 times.
✗ Branch 29 → 31 not taken.
✓ Branch 34 → 35 taken 271 times.
✗ Branch 34 → 36 not taken.
✓ Branch 36 → 37 taken 271 times.
✗ Branch 36 → 38 not taken.
✓ Branch 38 → 39 taken 271 times.
✗ Branch 38 → 42 not taken.
✓ Branch 40 → 41 taken 1084 times.
✓ Branch 40 → 42 taken 271 times.
✓ Branch 42 → 43 taken 271 times.
✗ Branch 42 → 44 not taken.
✗ Branch 64 → 65 not taken.
✗ Branch 64 → 66 not taken.
✗ Branch 68 → 69 not taken.
✗ Branch 68 → 70 not taken.
✗ Branch 72 → 73 not taken.
✗ Branch 72 → 74 not taken.
✗ Branch 76 → 77 not taken.
✗ Branch 76 → 80 not taken.
✗ Branch 78 → 79 not taken.
✗ Branch 78 → 80 not taken.
|
1626 | exec(QStringLiteral("INSERT INTO stats_hourly (scope, hour, name, value) VALUES (?, ?, ?, ?) " |
| 284 | "ON CONFLICT(scope, hour, name) DO UPDATE SET value = value + excluded.value"), | ||
| 285 |
1/2✓ Branch 32 → 33 taken 271 times.
✗ Branch 32 → 34 not taken.
|
542 | {scope, hourOf(now), name, delta}); |
| 286 | 813 | } | |
| 287 | |||
| 288 | 159 | qint64 DavPushStore::counter(const QString &scope, const QString &name) const { | |
| 289 |
1/1✓ Branch 2 → 3 taken 159 times.
|
159 | QSqlQuery q(m_db); |
| 290 |
1/1✓ Branch 6 → 7 taken 159 times.
|
159 | q.prepare(QStringLiteral("SELECT value FROM stats_counters WHERE scope = ? AND name = ?")); |
| 291 |
1/1✓ Branch 11 → 12 taken 159 times.
|
159 | q.addBindValue(scope); |
| 292 |
1/1✓ Branch 15 → 16 taken 159 times.
|
159 | q.addBindValue(name); |
| 293 |
9/12✓ Branch 17 → 18 taken 159 times.
✓ Branch 18 → 19 taken 159 times.
✗ Branch 18 → 24 not taken.
✓ Branch 19 → 20 taken 159 times.
✓ Branch 20 → 21 taken 6 times.
✓ Branch 20 → 24 taken 153 times.
✓ Branch 21 → 22 taken 6 times.
✓ Branch 22 → 23 taken 6 times.
✓ Branch 26 → 27 taken 6 times.
✓ Branch 26 → 28 taken 153 times.
✗ Branch 45 → 46 not taken.
✗ Branch 45 → 47 not taken.
|
318 | return q.exec() && q.next() ? q.value(0).toLongLong() : 0; |
| 294 | 159 | } | |
| 295 | |||
| 296 | 37 | qint64 DavPushStore::periodSum(const QString &scope, const QString &name, qint64 since) const { | |
| 297 |
1/1✓ Branch 2 → 3 taken 37 times.
|
37 | QSqlQuery q(m_db); |
| 298 |
1/1✓ Branch 6 → 7 taken 37 times.
|
37 | q.prepare(QStringLiteral("SELECT COALESCE(SUM(value), 0) FROM stats_hourly WHERE scope = ? AND name = ? AND hour >= ?")); |
| 299 |
1/1✓ Branch 11 → 12 taken 37 times.
|
37 | q.addBindValue(scope); |
| 300 |
1/1✓ Branch 15 → 16 taken 37 times.
|
37 | q.addBindValue(name); |
| 301 |
1/1✓ Branch 20 → 21 taken 37 times.
|
37 | q.addBindValue(hourOf(since)); |
| 302 |
7/12✓ Branch 22 → 23 taken 37 times.
✓ Branch 23 → 24 taken 37 times.
✗ Branch 23 → 29 not taken.
✓ Branch 24 → 25 taken 37 times.
✓ Branch 25 → 26 taken 37 times.
✗ Branch 25 → 29 not taken.
✓ Branch 26 → 27 taken 37 times.
✓ Branch 27 → 28 taken 37 times.
✓ Branch 31 → 32 taken 37 times.
✗ Branch 31 → 33 not taken.
✗ Branch 54 → 55 not taken.
✗ Branch 54 → 56 not taken.
|
74 | return q.exec() && q.next() ? q.value(0).toLongLong() : 0; |
| 303 | 37 | } | |
| 304 | |||
| 305 | 321 | bool DavPushStore::addLatency(const QString &scope, const QString &kind, qint64 millis, qint64 now) { | |
| 306 |
5/18✓ Branch 37 → 38 taken 321 times.
✗ Branch 37 → 39 not taken.
✓ Branch 39 → 40 taken 321 times.
✗ Branch 39 → 41 not taken.
✓ Branch 41 → 42 taken 321 times.
✗ Branch 41 → 45 not taken.
✓ Branch 43 → 44 taken 1284 times.
✓ Branch 43 → 45 taken 321 times.
✗ Branch 71 → 72 not taken.
✗ Branch 71 → 73 not taken.
✗ Branch 75 → 76 not taken.
✗ Branch 75 → 77 not taken.
✗ Branch 79 → 80 not taken.
✗ Branch 79 → 81 not taken.
✗ Branch 83 → 84 not taken.
✗ Branch 83 → 87 not taken.
✗ Branch 85 → 86 not taken.
✗ Branch 85 → 87 not taken.
|
1926 | return exec(QStringLiteral("INSERT INTO stats_latency (scope, kind, millis, at) VALUES (?, ?, ?, ?)"), |
| 307 |
2/3✓ Branch 10 → 11 taken 321 times.
✓ Branch 11 → 12 taken 321 times.
✗ Branch 11 → 24 not taken.
|
1284 | {scope, kind, millis, now}) && |
| 308 |
8/23✓ Branch 21 → 22 taken 321 times.
✓ Branch 22 → 23 taken 321 times.
✗ Branch 22 → 24 not taken.
✓ Branch 27 → 28 taken 321 times.
✗ Branch 27 → 29 not taken.
✓ Branch 29 → 30 taken 321 times.
✗ Branch 29 → 31 not taken.
✓ Branch 31 → 32 taken 321 times.
✗ Branch 31 → 35 not taken.
✓ Branch 33 → 34 taken 1605 times.
✓ Branch 33 → 35 taken 321 times.
✓ Branch 35 → 36 taken 321 times.
✗ Branch 35 → 37 not taken.
✗ Branch 47 → 48 not taken.
✗ Branch 47 → 49 not taken.
✗ Branch 51 → 52 not taken.
✗ Branch 51 → 53 not taken.
✗ Branch 55 → 56 not taken.
✗ Branch 55 → 57 not taken.
✗ Branch 59 → 60 not taken.
✗ Branch 59 → 63 not taken.
✗ Branch 61 → 62 not taken.
✗ Branch 61 → 63 not taken.
|
2247 | exec(QStringLiteral("DELETE FROM stats_latency WHERE scope = ? AND kind = ? AND id NOT IN " |
| 309 | "(SELECT id FROM stats_latency WHERE scope = ? AND kind = ? ORDER BY id DESC LIMIT ?)"), | ||
| 310 |
1/2✓ Branch 25 → 26 taken 321 times.
✗ Branch 25 → 27 not taken.
|
642 | {scope, kind, scope, kind, kMaxLatencySamples}); |
| 311 | 642 | } | |
| 312 | |||
| 313 | 11 | QList<qint64> DavPushStore::latencies(const QString &scope, const QString &kind) const { | |
| 314 | 11 | QList<qint64> values; | |
| 315 |
1/1✓ Branch 2 → 3 taken 11 times.
|
11 | QSqlQuery q(m_db); |
| 316 |
1/1✓ Branch 6 → 7 taken 11 times.
|
11 | q.prepare(QStringLiteral("SELECT millis FROM stats_latency WHERE scope = ? AND kind = ? ORDER BY id")); |
| 317 |
1/1✓ Branch 11 → 12 taken 11 times.
|
11 | q.addBindValue(scope); |
| 318 |
1/1✓ Branch 15 → 16 taken 11 times.
|
11 | q.addBindValue(kind); |
| 319 |
2/3✓ Branch 17 → 18 taken 11 times.
✓ Branch 18 → 19 taken 11 times.
✗ Branch 18 → 27 not taken.
|
11 | if (q.exec()) |
| 320 |
6/6✓ Branch 20 → 21 taken 276 times.
✓ Branch 21 → 22 taken 276 times.
✓ Branch 22 → 23 taken 276 times.
✓ Branch 25 → 26 taken 287 times.
✓ Branch 26 → 20 taken 276 times.
✓ Branch 26 → 27 taken 11 times.
|
287 | while (q.next()) values << q.value(0).toLongLong(); |
| 321 | 11 | return values; | |
| 322 | 11 | } | |
| 323 | |||
| 324 | 264 | bool DavPushStore::addEvent(const QString &scope, const QString &kind, const QString &detail, qint64 now) { | |
| 325 |
5/18✓ Branch 36 → 37 taken 264 times.
✗ Branch 36 → 38 not taken.
✓ Branch 38 → 39 taken 264 times.
✗ Branch 38 → 40 not taken.
✓ Branch 40 → 41 taken 264 times.
✗ Branch 40 → 44 not taken.
✓ Branch 42 → 43 taken 1056 times.
✓ Branch 42 → 44 taken 264 times.
✗ Branch 72 → 73 not taken.
✗ Branch 72 → 74 not taken.
✗ Branch 76 → 77 not taken.
✗ Branch 76 → 78 not taken.
✗ Branch 80 → 81 not taken.
✗ Branch 80 → 82 not taken.
✗ Branch 84 → 85 not taken.
✗ Branch 84 → 88 not taken.
✗ Branch 86 → 87 not taken.
✗ Branch 86 → 88 not taken.
|
1584 | return exec(QStringLiteral("INSERT INTO stats_events (scope, at, kind, detail) VALUES (?, ?, ?, ?)"), |
| 326 |
3/7✓ Branch 11 → 12 taken 264 times.
✓ Branch 12 → 13 taken 264 times.
✗ Branch 12 → 23 not taken.
✓ Branch 44 → 45 taken 264 times.
✗ Branch 44 → 46 not taken.
✗ Branch 90 → 91 not taken.
✗ Branch 90 → 92 not taken.
|
1320 | {scope, now, kind, detail.left(300)}) && |
| 327 |
8/23✓ Branch 20 → 21 taken 264 times.
✓ Branch 21 → 22 taken 264 times.
✗ Branch 21 → 23 not taken.
✓ Branch 26 → 27 taken 264 times.
✗ Branch 26 → 28 not taken.
✓ Branch 28 → 29 taken 264 times.
✗ Branch 28 → 30 not taken.
✓ Branch 30 → 31 taken 264 times.
✗ Branch 30 → 34 not taken.
✓ Branch 32 → 33 taken 792 times.
✓ Branch 32 → 34 taken 264 times.
✓ Branch 34 → 35 taken 264 times.
✗ Branch 34 → 36 not taken.
✗ Branch 48 → 49 not taken.
✗ Branch 48 → 50 not taken.
✗ Branch 52 → 53 not taken.
✗ Branch 52 → 54 not taken.
✗ Branch 56 → 57 not taken.
✗ Branch 56 → 58 not taken.
✗ Branch 60 → 61 not taken.
✗ Branch 60 → 64 not taken.
✗ Branch 62 → 63 not taken.
✗ Branch 62 → 64 not taken.
|
1320 | exec(QStringLiteral("DELETE FROM stats_events WHERE scope = ? AND id NOT IN " |
| 328 | "(SELECT id FROM stats_events WHERE scope = ? ORDER BY id DESC LIMIT ?)"), | ||
| 329 |
1/2✓ Branch 24 → 25 taken 264 times.
✗ Branch 24 → 26 not taken.
|
528 | {scope, scope, kMaxEvents}); |
| 330 | 528 | } | |
| 331 | |||
| 332 | 5 | QList<DavPushStore::Event> DavPushStore::events(const QString &scope) const { | |
| 333 | 5 | QList<Event> result; | |
| 334 |
1/1✓ Branch 2 → 3 taken 5 times.
|
5 | QSqlQuery q(m_db); |
| 335 |
1/1✓ Branch 6 → 7 taken 5 times.
|
5 | q.prepare(QStringLiteral("SELECT at, kind, detail FROM stats_events WHERE scope = ? ORDER BY id DESC")); |
| 336 |
1/1✓ Branch 11 → 12 taken 5 times.
|
5 | q.addBindValue(scope); |
| 337 |
2/3✓ Branch 13 → 14 taken 5 times.
✓ Branch 14 → 15 taken 5 times.
✗ Branch 14 → 30 not taken.
|
5 | if (q.exec()) |
| 338 |
5/5✓ Branch 16 → 17 taken 100 times.
✓ Branch 17 → 18 taken 100 times.
✓ Branch 28 → 29 taken 105 times.
✓ Branch 29 → 16 taken 100 times.
✓ Branch 29 → 30 taken 5 times.
|
105 | while (q.next()) result.append({q.value(0).toLongLong(), q.value(1).toString(), q.value(2).toString()}); |
| 339 | 5 | return result; | |
| 340 | 105 | } | |
| 341 | |||
| 342 | 2 | bool DavPushStore::resetStatistics(const QString &scope, qint64 now) { | |
| 343 |
1/2✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2 times.
|
2 | if (!m_db.transaction()) return false; |
| 344 |
5/18✓ Branch 85 → 86 taken 2 times.
✗ Branch 85 → 87 not taken.
✓ Branch 87 → 88 taken 2 times.
✗ Branch 87 → 89 not taken.
✓ Branch 89 → 90 taken 2 times.
✗ Branch 89 → 93 not taken.
✓ Branch 91 → 92 taken 2 times.
✓ Branch 91 → 93 taken 2 times.
✗ Branch 195 → 196 not taken.
✗ Branch 195 → 197 not taken.
✗ Branch 199 → 200 not taken.
✗ Branch 199 → 201 not taken.
✗ Branch 203 → 204 not taken.
✗ Branch 203 → 205 not taken.
✗ Branch 207 → 208 not taken.
✗ Branch 207 → 211 not taken.
✗ Branch 209 → 210 not taken.
✗ Branch 209 → 211 not taken.
|
8 | const bool ok = exec(QStringLiteral("DELETE FROM stats_counters WHERE scope = ?"), {scope}) && |
| 345 |
8/23✓ Branch 17 → 18 taken 2 times.
✓ Branch 18 → 19 taken 2 times.
✗ Branch 18 → 42 not taken.
✓ Branch 75 → 76 taken 2 times.
✗ Branch 75 → 77 not taken.
✓ Branch 77 → 78 taken 2 times.
✗ Branch 77 → 79 not taken.
✓ Branch 79 → 80 taken 2 times.
✗ Branch 79 → 83 not taken.
✓ Branch 81 → 82 taken 2 times.
✓ Branch 81 → 83 taken 2 times.
✓ Branch 83 → 84 taken 2 times.
✗ Branch 83 → 85 not taken.
✗ Branch 171 → 172 not taken.
✗ Branch 171 → 173 not taken.
✗ Branch 175 → 176 not taken.
✗ Branch 175 → 177 not taken.
✗ Branch 179 → 180 not taken.
✗ Branch 179 → 181 not taken.
✗ Branch 183 → 184 not taken.
✗ Branch 183 → 187 not taken.
✗ Branch 185 → 186 not taken.
✗ Branch 185 → 187 not taken.
|
8 | exec(QStringLiteral("DELETE FROM stats_hourly WHERE scope = ?"), {scope}) && |
| 346 |
8/23✓ Branch 24 → 25 taken 2 times.
✓ Branch 25 → 26 taken 2 times.
✗ Branch 25 → 42 not taken.
✓ Branch 65 → 66 taken 2 times.
✗ Branch 65 → 67 not taken.
✓ Branch 67 → 68 taken 2 times.
✗ Branch 67 → 69 not taken.
✓ Branch 69 → 70 taken 2 times.
✗ Branch 69 → 73 not taken.
✓ Branch 71 → 72 taken 2 times.
✓ Branch 71 → 73 taken 2 times.
✓ Branch 73 → 74 taken 2 times.
✗ Branch 73 → 75 not taken.
✗ Branch 147 → 148 not taken.
✗ Branch 147 → 149 not taken.
✗ Branch 151 → 152 not taken.
✗ Branch 151 → 153 not taken.
✗ Branch 155 → 156 not taken.
✗ Branch 155 → 157 not taken.
✗ Branch 159 → 160 not taken.
✗ Branch 159 → 163 not taken.
✗ Branch 161 → 162 not taken.
✗ Branch 161 → 163 not taken.
|
8 | exec(QStringLiteral("DELETE FROM stats_latency WHERE scope = ?"), {scope}) && |
| 347 |
10/26✓ Branch 10 → 11 taken 2 times.
✓ Branch 11 → 12 taken 2 times.
✗ Branch 11 → 42 not taken.
✓ Branch 31 → 32 taken 2 times.
✓ Branch 32 → 33 taken 2 times.
✗ Branch 32 → 42 not taken.
✓ Branch 55 → 56 taken 2 times.
✗ Branch 55 → 57 not taken.
✓ Branch 57 → 58 taken 2 times.
✗ Branch 57 → 59 not taken.
✓ Branch 59 → 60 taken 2 times.
✗ Branch 59 → 63 not taken.
✓ Branch 61 → 62 taken 2 times.
✓ Branch 61 → 63 taken 2 times.
✓ Branch 63 → 64 taken 2 times.
✗ Branch 63 → 65 not taken.
✗ Branch 123 → 124 not taken.
✗ Branch 123 → 125 not taken.
✗ Branch 127 → 128 not taken.
✗ Branch 127 → 129 not taken.
✗ Branch 131 → 132 not taken.
✗ Branch 131 → 133 not taken.
✗ Branch 135 → 136 not taken.
✗ Branch 135 → 139 not taken.
✗ Branch 137 → 138 not taken.
✗ Branch 137 → 139 not taken.
|
14 | exec(QStringLiteral("DELETE FROM stats_events WHERE scope = ?"), {scope}) && |
| 348 |
9/25✓ Branch 39 → 40 taken 2 times.
✓ Branch 40 → 41 taken 2 times.
✗ Branch 40 → 42 not taken.
✓ Branch 43 → 44 taken 2 times.
✗ Branch 43 → 45 not taken.
✓ Branch 45 → 46 taken 2 times.
✗ Branch 45 → 47 not taken.
✓ Branch 47 → 48 taken 2 times.
✗ Branch 47 → 49 not taken.
✓ Branch 49 → 50 taken 2 times.
✗ Branch 49 → 53 not taken.
✓ Branch 51 → 52 taken 4 times.
✓ Branch 51 → 53 taken 2 times.
✓ Branch 53 → 54 taken 2 times.
✗ Branch 53 → 55 not taken.
✗ Branch 99 → 100 not taken.
✗ Branch 99 → 101 not taken.
✗ Branch 103 → 104 not taken.
✗ Branch 103 → 105 not taken.
✗ Branch 107 → 108 not taken.
✗ Branch 107 → 109 not taken.
✗ Branch 111 → 112 not taken.
✗ Branch 111 → 115 not taken.
✗ Branch 113 → 114 not taken.
✗ Branch 113 → 115 not taken.
|
8 | exec(QStringLiteral("INSERT OR REPLACE INTO stats_scopes (scope, since) VALUES (?, ?)"), {scope, now}); |
| 349 |
1/2✗ Branch 93 → 94 not taken.
✓ Branch 93 → 96 taken 2 times.
|
2 | if (!ok) { |
| 350 | ✗ | m_db.rollback(); | |
| 351 | ✗ | return false; | |
| 352 | } | ||
| 353 | 2 | return m_db.commit(); | |
| 354 | 10 | } | |
| 355 | |||
| 356 | 4 | qint64 DavPushStore::statisticsSince(const QString &scope) const { | |
| 357 |
1/1✓ Branch 2 → 3 taken 4 times.
|
4 | QSqlQuery q(m_db); |
| 358 |
1/1✓ Branch 6 → 7 taken 4 times.
|
4 | q.prepare(QStringLiteral("SELECT since FROM stats_scopes WHERE scope = ?")); |
| 359 |
1/1✓ Branch 11 → 12 taken 4 times.
|
4 | q.addBindValue(scope); |
| 360 |
9/12✓ Branch 13 → 14 taken 4 times.
✓ Branch 14 → 15 taken 4 times.
✗ Branch 14 → 20 not taken.
✓ Branch 15 → 16 taken 4 times.
✓ Branch 16 → 17 taken 2 times.
✓ Branch 16 → 20 taken 2 times.
✓ Branch 17 → 18 taken 2 times.
✓ Branch 18 → 19 taken 2 times.
✓ Branch 22 → 23 taken 2 times.
✓ Branch 22 → 24 taken 2 times.
✗ Branch 37 → 38 not taken.
✗ Branch 37 → 39 not taken.
|
8 | return q.exec() && q.next() ? q.value(0).toLongLong() : 0; |
| 361 | 4 | } | |
| 362 | |||
| 363 | 36 | bool DavPushStore::pruneHourly(qint64 now) { | |
| 364 |
3/5✓ Branch 8 → 9 taken 36 times.
✓ Branch 14 → 15 taken 36 times.
✓ Branch 14 → 16 taken 36 times.
✗ Branch 27 → 28 not taken.
✗ Branch 27 → 29 not taken.
|
108 | return exec(QStringLiteral("DELETE FROM stats_hourly WHERE hour < ?"), |
| 365 | 72 | {hourOf(now) - qint64(kHourlyRetentionHours) * 3600}); | |
| 366 | 36 | } | |
| 367 |