diff --git a/src/engine/imap-db/imap-db-folder.vala b/src/engine/imap-db/imap-db-folder.vala index fd796796..de015f5a 100644 --- a/src/engine/imap-db/imap-db-folder.vala +++ b/src/engine/imap-db/imap-db-folder.vala @@ -43,6 +43,10 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { private const int CREATE_MERGE_EMAIL_CHUNK_COUNT = 25; private const int OLD_MSG_DETACH_BATCH_SIZE = 1000; + // When old messages beyond the period set in the account preferences are removed this number + // are retained even if they are beyond the threshold. + private const int MINIMUM_MESSAGES_TO_RETAIN_DURING_GC = 100; + [Flags] public enum ListFlags { NONE = 0, @@ -943,22 +947,42 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics { // UIDs not guaranteed to be in order. StringBuilder sql = new StringBuilder(); sql.append(""" - SELECT id, message_id, ordering - FROM MessageLocationTable - WHERE folder_id = ? - AND message_id IN ( - SELECT id - FROM MessageTable - INDEXED BY MessageTableInternalDateTimeTIndex - WHERE internaldate_time_t < ? - ) + SELECT COUNT(*) + FROM MessageLocationTable ml + INNER JOIN MessageTable m + INDEXED BY MessageTableInternalDateTimeTIndex + ON ml.message_id = m.id + WHERE ml.folder_id = ? + AND m.internaldate_time_t >= ? """); Db.Statement stmt = cx.prepare(sql.str); stmt.bind_rowid(0, folder_id); stmt.bind_int64(1, cutoff.to_unix()); - Db.Result results = stmt.exec(cancellable); + int64 found_within_threshold = results.int64_at(0); + int64 extra_to_retain = + (MINIMUM_MESSAGES_TO_RETAIN_DURING_GC - found_within_threshold).clamp(0, int64.MAX); + + sql = new StringBuilder(); + sql.append(""" + SELECT ml.id, ml.message_id, ml.ordering + FROM MessageLocationTable ml + INNER JOIN MessageTable m + INDEXED BY MessageTableInternalDateTimeTIndex + ON ml.message_id = m.id + WHERE ml.folder_id = ? + AND m.internaldate_time_t < ? + ORDER BY m.internaldate_time_t DESC + LIMIT -1 OFFSET ?; + """); + + stmt = cx.prepare(sql.str); + stmt.bind_rowid(0, folder_id); + stmt.bind_int64(1, cutoff.to_unix()); + stmt.bind_int64(2, extra_to_retain); + + results = stmt.exec(cancellable); while (!results.finished) { if (deleted_email_ids == null) { diff --git a/test/engine/imap-db/imap-db-folder-test.vala b/test/engine/imap-db/imap-db-folder-test.vala index be02bcb5..bce1fb43 100644 --- a/test/engine/imap-db/imap-db-folder-test.vala +++ b/test/engine/imap-db/imap-db-folder-test.vala @@ -366,6 +366,22 @@ class Geary.ImapDB.FolderTest : TestCase { (3, 3, 1, 2, 1); """); + for (int i = 4; i <= 200; i++) { + this.account.db.exec( + "INSERT INTO MessageTable (id, fields, to_field, internaldate_time_t) " + + "VALUES (%d, %d, '%s', %s);".printf(i, + fixture_fields, + fixture_to, + beyond_threshold.to_unix().to_string()) + ); + this.account.db.exec( + "INSERT INTO MessageLocationTable " + + " (id, message_id, folder_id, ordering, remove_marker) " + + "VALUES (%d, %d, 1, %d, 1);".printf(i, i, i) + ); + } + + this.folder.detach_emails_before_timestamp.begin( threshold, null, @@ -375,7 +391,7 @@ class Geary.ImapDB.FolderTest : TestCase { int64[] expected = { 1, 2 }; Db.Result result = this.account.db.query( - "SELECT id FROM MessageLocationTable" + "SELECT id FROM MessageLocationTable WHERE id IN (1, 2);" ); int i = 0; @@ -386,6 +402,13 @@ class Geary.ImapDB.FolderTest : TestCase { result.next(); } assert_true(i == expected.length, "Not enough rows"); + + result = this.account.db.query( + "SELECT COUNT(id) FROM MessageLocationTable WHERE folder_id = 1;" + ); + assert_false(result.finished); + assert_equal(result.int64_at(0), 100); + result.next(); } private Email new_mock_remote_email(int64 uid,