Full normalization when marked messages are not removed: Closes #7402
Because full folder normalization is avoided whenever possible, need to go back to marking messages as removed in the database (vs. just in memory) so if messages are not removed on the server (or the response was not received for some reason), the folder is seen as "dirty" and a full normalization is processed.
This commit is contained in:
parent
8d1f7a2b26
commit
3abbb95c98
4 changed files with 157 additions and 111 deletions
|
|
@ -59,11 +59,13 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
public int64 message_id;
|
||||
public Imap.UID uid;
|
||||
public ImapDB.EmailIdentifier email_id;
|
||||
public bool marked_removed;
|
||||
|
||||
public LocationIdentifier(int64 message_id, Imap.UID uid) {
|
||||
public LocationIdentifier(int64 message_id, Imap.UID uid, bool marked_removed) {
|
||||
this.message_id = message_id;
|
||||
this.uid = uid;
|
||||
this.email_id = new ImapDB.EmailIdentifier(message_id, uid);
|
||||
email_id = new ImapDB.EmailIdentifier(message_id, uid);
|
||||
this.marked_removed = marked_removed;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +77,6 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
private string account_owner_email;
|
||||
private int64 folder_id;
|
||||
private Geary.Imap.FolderProperties properties;
|
||||
private Gee.HashSet<Imap.UID> marked_removed = new Gee.HashSet<Imap.UID>();
|
||||
|
||||
/**
|
||||
* Fired after one or more emails have been fetched with all Fields, and
|
||||
|
|
@ -113,49 +114,6 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
this.properties = properties;
|
||||
}
|
||||
|
||||
// Should be called whenever the main Geary.Folder interface is opened or closed
|
||||
public void reset(){
|
||||
lock (marked_removed) {
|
||||
marked_removed.clear();
|
||||
}
|
||||
|
||||
// TODO: Wait for all I/O to complete before exiting
|
||||
}
|
||||
|
||||
// Returns true if the EmailIdentifier was marked before being removed
|
||||
private bool unmark_removed(Imap.UID uid) {
|
||||
lock (marked_removed) {
|
||||
return marked_removed.remove(uid);
|
||||
}
|
||||
}
|
||||
|
||||
private void mark_unmark_removed(Gee.Collection<Imap.UID> uids, bool mark) {
|
||||
lock (marked_removed) {
|
||||
if (mark)
|
||||
marked_removed.add_all(uids);
|
||||
else
|
||||
marked_removed.remove_all(uids);
|
||||
}
|
||||
}
|
||||
|
||||
private void clear_marked_removed() {
|
||||
lock (marked_removed) {
|
||||
marked_removed.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private bool is_marked_removed(Imap.UID uid) {
|
||||
lock (marked_removed) {
|
||||
return marked_removed.contains(uid);
|
||||
}
|
||||
}
|
||||
|
||||
private int get_marked_removed_count() {
|
||||
lock (marked_removed) {
|
||||
return marked_removed.size;
|
||||
}
|
||||
}
|
||||
|
||||
public async int get_email_count_async(ListFlags flags, Cancellable? cancellable) throws Error {
|
||||
int count = 0;
|
||||
yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => {
|
||||
|
|
@ -275,8 +233,10 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
// convert initial_id into UID to start walking the list
|
||||
Imap.UID? start_uid = null;
|
||||
if (initial_id != null) {
|
||||
// use INCLUDE_MARKED_FOR_REMOVE because this is a ranged list ...
|
||||
// do_results_to_location() will deal with removing EmailIdentifiers if necessary
|
||||
LocationIdentifier? location = do_get_location_for_id(cx, initial_id,
|
||||
ListFlags.NONE, cancellable);
|
||||
ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable);
|
||||
if (location == null)
|
||||
return Db.TransactionOutcome.DONE;
|
||||
|
||||
|
|
@ -296,7 +256,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
}
|
||||
|
||||
StringBuilder sql = new StringBuilder("""
|
||||
SELECT MessageLocationTable.message_id, ordering
|
||||
SELECT MessageLocationTable.message_id, ordering, remove_marker
|
||||
FROM MessageLocationTable
|
||||
""");
|
||||
if (only_incomplete) {
|
||||
|
|
@ -348,15 +308,18 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
// database ... first, gather locations of all emails in database
|
||||
Gee.List<LocationIdentifier>? locations = null;
|
||||
yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => {
|
||||
LocationIdentifier? start_location = do_get_location_for_id(cx, start_id, ListFlags.NONE,
|
||||
cancellable);
|
||||
// use INCLUDE_MARKED_FOR_REMOVE because this is a ranged list ...
|
||||
// do_results_to_location() will deal with removing EmailIdentifiers if necessary
|
||||
LocationIdentifier? start_location = do_get_location_for_id(cx, start_id,
|
||||
ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable);
|
||||
if (start_location == null)
|
||||
return Db.TransactionOutcome.DONE;
|
||||
|
||||
Imap.UID start_uid = start_location.uid;
|
||||
|
||||
LocationIdentifier? end_location = do_get_location_for_id(cx, end_id, ListFlags.NONE,
|
||||
cancellable);
|
||||
// see note above about INCLUDE_MARKED_FOR_REMOVE
|
||||
LocationIdentifier? end_location = do_get_location_for_id(cx, end_id,
|
||||
ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable);
|
||||
if (end_location == null)
|
||||
return Db.TransactionOutcome.DONE;
|
||||
|
||||
|
|
@ -371,7 +334,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
return Db.TransactionOutcome.DONE;
|
||||
|
||||
Db.Statement stmt = cx.prepare("""
|
||||
SELECT message_id, ordering
|
||||
SELECT message_id, ordering, remove_marker
|
||||
FROM MessageLocationTable
|
||||
WHERE folder_id = ? AND ordering >= ? AND ordering <= ?
|
||||
""");
|
||||
|
|
@ -412,7 +375,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
Gee.List<LocationIdentifier>? locations = null;
|
||||
yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => {
|
||||
StringBuilder sql = new StringBuilder("""
|
||||
SELECT MessageLocationTable.message_id, ordering
|
||||
SELECT MessageLocationTable.message_id, ordering, remove_marker
|
||||
FROM MessageLocationTable
|
||||
""");
|
||||
|
||||
|
|
@ -453,7 +416,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
Gee.List<LocationIdentifier> locations = new Gee.ArrayList<LocationIdentifier>();
|
||||
yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => {
|
||||
StringBuilder sql = new StringBuilder("""
|
||||
SELECT MessageLocationTable.message_id, ordering
|
||||
SELECT MessageLocationTable.message_id, ordering, remove_marker
|
||||
FROM MessageLocationTable
|
||||
""");
|
||||
|
||||
|
|
@ -548,8 +511,9 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
return email;
|
||||
}
|
||||
|
||||
// Note that this does INCLUDES messages marked for removal
|
||||
public async Gee.SortedSet<Imap.UID>? list_uids_by_range_async(Imap.UID first_uid, Imap.UID last_uid,
|
||||
Cancellable? cancellable) throws Error {
|
||||
bool include_marked_for_removal, Cancellable? cancellable) throws Error {
|
||||
// order correctly
|
||||
Imap.UID start, end;
|
||||
if (first_uid.compare_to(last_uid) < 0) {
|
||||
|
|
@ -563,7 +527,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
Gee.SortedSet<Imap.UID> uids = new Gee.TreeSet<Imap.UID>();
|
||||
yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => {
|
||||
Db.Statement stmt = cx.prepare("""
|
||||
SELECT ordering
|
||||
SELECT ordering, remove_marker
|
||||
FROM MessageLocationTable
|
||||
WHERE folder_id = ? AND ordering >= ? AND ordering <= ?
|
||||
""");
|
||||
|
|
@ -573,7 +537,9 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
|
||||
Db.Result result = stmt.exec(cancellable);
|
||||
while (!result.finished) {
|
||||
uids.add(new Imap.UID(result.int64_at(0)));
|
||||
if (include_marked_for_removal || !result.bool_at(1))
|
||||
uids.add(new Imap.UID(result.int64_at(0)));
|
||||
|
||||
result.next(cancellable);
|
||||
}
|
||||
|
||||
|
|
@ -645,11 +611,11 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
}
|
||||
|
||||
// Returns null if the UID is not found in this Folder.
|
||||
public async ImapDB.EmailIdentifier? get_id_async(Imap.UID uid, Cancellable? cancellable)
|
||||
throws Error {
|
||||
public async ImapDB.EmailIdentifier? get_id_async(Imap.UID uid, ListFlags flags,
|
||||
Cancellable? cancellable) throws Error {
|
||||
ImapDB.EmailIdentifier? id = null;
|
||||
yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => {
|
||||
LocationIdentifier? location = do_get_location_for_uid(cx, uid, ListFlags.NONE,
|
||||
LocationIdentifier? location = do_get_location_for_uid(cx, uid, flags,
|
||||
cancellable);
|
||||
if (location != null)
|
||||
id = location.email_id;
|
||||
|
|
@ -660,12 +626,12 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
return id;
|
||||
}
|
||||
|
||||
public async Gee.Set<ImapDB.EmailIdentifier> get_ids_async(Gee.Collection<Imap.UID> uids,
|
||||
Cancellable? cancellable) throws Error {
|
||||
public async Gee.Set<ImapDB.EmailIdentifier>? get_ids_async(Gee.Collection<Imap.UID> uids,
|
||||
ListFlags flags, Cancellable? cancellable) throws Error {
|
||||
Gee.Set<ImapDB.EmailIdentifier> ids = new Gee.HashSet<ImapDB.EmailIdentifier>();
|
||||
yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => {
|
||||
foreach (Imap.UID uid in uids) {
|
||||
LocationIdentifier? location = do_get_location_for_uid(cx, uid, ListFlags.NONE,
|
||||
LocationIdentifier? location = do_get_location_for_uid(cx, uid, flags,
|
||||
cancellable);
|
||||
if (location != null)
|
||||
ids.add(location.email_id);
|
||||
|
|
@ -724,8 +690,8 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
// remove one at a time, gather UIDs
|
||||
Gee.HashSet<Imap.UID> uids = new Gee.HashSet<Imap.UID>();
|
||||
foreach (ImapDB.EmailIdentifier id in ids) {
|
||||
LocationIdentifier? location = do_get_location_for_id(cx, id, ListFlags.NONE,
|
||||
cancellable);
|
||||
LocationIdentifier? location = do_get_location_for_id(cx, id,
|
||||
ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable);
|
||||
if (location == null)
|
||||
continue;
|
||||
|
||||
|
|
@ -737,9 +703,6 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
uids.add(location.uid);
|
||||
}
|
||||
|
||||
// Remove any that may have been marked removed
|
||||
mark_unmark_removed(uids, false);
|
||||
|
||||
return Db.TransactionOutcome.COMMIT;
|
||||
}, cancellable);
|
||||
}
|
||||
|
|
@ -750,8 +713,6 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
"DELETE FROM MessageLocationTable WHERE folder_id=?");
|
||||
stmt.bind_rowid(0, folder_id);
|
||||
|
||||
clear_marked_removed();
|
||||
|
||||
return Db.TransactionOutcome.COMMIT;
|
||||
}, cancellable);
|
||||
}
|
||||
|
|
@ -882,7 +843,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
was_unread = true;
|
||||
}
|
||||
|
||||
internal_is_marked = unmark_removed(location.uid);
|
||||
internal_is_marked = location.marked_removed;
|
||||
|
||||
do_remove_association_with_folder(cx, location, cancellable);
|
||||
|
||||
|
|
@ -905,18 +866,19 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
Gee.Collection<ImapDB.EmailIdentifier> ids, bool mark_removed, Cancellable? cancellable)
|
||||
throws Error {
|
||||
Gee.Set<ImapDB.EmailIdentifier> removed_ids = new Gee.HashSet<ImapDB.EmailIdentifier>();
|
||||
yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => {
|
||||
yield db.exec_transaction_async(Db.TransactionType.RW, (cx) => {
|
||||
Gee.HashSet<Imap.UID> uids = new Gee.HashSet<Imap.UID>();
|
||||
foreach (ImapDB.EmailIdentifier id in ids) {
|
||||
LocationIdentifier? location = do_get_location_for_id(cx, id, ListFlags.NONE,
|
||||
cancellable);
|
||||
LocationIdentifier? location = do_get_location_for_id(cx, id,
|
||||
ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable);
|
||||
if (location != null) {
|
||||
uids.add(location.uid);
|
||||
removed_ids.add(location.email_id);
|
||||
}
|
||||
}
|
||||
|
||||
mark_unmark_removed(uids, mark_removed);
|
||||
if (uids.size > 0)
|
||||
do_mark_unmark_removed(cx, uids, mark_removed, cancellable);
|
||||
|
||||
return Db.TransactionOutcome.DONE;
|
||||
}, cancellable);
|
||||
|
|
@ -924,8 +886,39 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
return (removed_ids.size > 0) ? removed_ids : null;
|
||||
}
|
||||
|
||||
// Returns the number of messages marked for removal in this folder
|
||||
public async int get_marked_for_remove_count_async(Cancellable? cancellable) throws Error {
|
||||
int count = 0;
|
||||
yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => {
|
||||
count = do_get_marked_removed_count(cx, cancellable);
|
||||
|
||||
return Db.TransactionOutcome.DONE;
|
||||
}, cancellable);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// Clears all remove markers from the folder
|
||||
public async void clear_remove_markers_async(Cancellable? cancellable) throws Error {
|
||||
yield db.exec_transaction_async(Db.TransactionType.WO, (cx) => {
|
||||
Db.Statement stmt = cx.prepare("""
|
||||
UPDATE MessageLocationTable
|
||||
SET remove_marker=?
|
||||
WHERE folder_id=? AND remove_marker <> ?
|
||||
""");
|
||||
stmt.bind_bool(0, false);
|
||||
stmt.bind_rowid(1, folder_id);
|
||||
stmt.bind_bool(2, false);
|
||||
|
||||
stmt.exec(cancellable);
|
||||
|
||||
return Db.TransactionOutcome.COMMIT;
|
||||
}, cancellable);
|
||||
}
|
||||
|
||||
public async Gee.Map<ImapDB.EmailIdentifier, Geary.Email.Field>? list_email_fields_by_id_async(
|
||||
Gee.Collection<ImapDB.EmailIdentifier> ids, Cancellable? cancellable) throws Error {
|
||||
Gee.Collection<ImapDB.EmailIdentifier> ids, ListFlags flags, Cancellable? cancellable)
|
||||
throws Error {
|
||||
if (ids.size == 0)
|
||||
return null;
|
||||
|
||||
|
|
@ -945,7 +938,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
"SELECT fields FROM MessageTable WHERE id = ?");
|
||||
|
||||
foreach (ImapDB.EmailIdentifier id in list) {
|
||||
LocationIdentifier? location_id = do_get_location_for_id(cx, id, ListFlags.NONE,
|
||||
LocationIdentifier? location_id = do_get_location_for_id(cx, id, flags,
|
||||
cancellable);
|
||||
if (location_id == null)
|
||||
continue;
|
||||
|
|
@ -987,11 +980,40 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
if (results.finished)
|
||||
return 0;
|
||||
|
||||
int marked = !flags.include_marked_for_remove() ? get_marked_removed_count() : 0;
|
||||
int marked = !flags.include_marked_for_remove() ? do_get_marked_removed_count(cx, cancellable) : 0;
|
||||
|
||||
return Numeric.int_floor(results.int_at(0) - marked, 0);
|
||||
}
|
||||
|
||||
private int do_get_marked_removed_count(Db.Connection cx, Cancellable? cancellable) throws Error {
|
||||
Db.Statement stmt = cx.prepare(
|
||||
"SELECT COUNT(*) FROM MessageLocationTable WHERE folder_id=? AND remove_marker <> ?");
|
||||
stmt.bind_rowid(0, folder_id);
|
||||
stmt.bind_bool(1, false);
|
||||
|
||||
Db.Result results = stmt.exec(cancellable);
|
||||
|
||||
return !results.finished ? results.int_at(0) : 0;
|
||||
}
|
||||
|
||||
private void do_mark_unmark_removed(Db.Connection cx, Gee.Collection<Imap.UID> uids,
|
||||
bool mark_removed, Cancellable? cancellable) throws Error {
|
||||
// prepare Statement for reuse
|
||||
Db.Statement stmt = cx.prepare(
|
||||
"UPDATE MessageLocationTable SET remove_marker=? WHERE folder_id=? AND ordering=?");
|
||||
stmt.bind_bool(0, mark_removed);
|
||||
stmt.bind_rowid(1, folder_id);
|
||||
|
||||
foreach (Imap.UID uid in uids) {
|
||||
stmt.bind_int64(2, uid.value);
|
||||
|
||||
stmt.exec(cancellable);
|
||||
|
||||
// keep folder_id and mark_removed, replace UID each iteration
|
||||
stmt.reset(Db.ResetScope.SAVE_BINDINGS);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns message_id if duplicate found, associated set to true if message is already associated
|
||||
// with this folder. Only call this on emails that came from the IMAP Folder.
|
||||
private LocationIdentifier? do_search_for_duplicates(Db.Connection cx, Geary.Email email,
|
||||
|
|
@ -1008,7 +1030,8 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
// See if it already exists; first by UID (which is only guaranteed to
|
||||
// be unique in a folder, not account-wide)
|
||||
if (email_id.uid != null)
|
||||
location = do_get_location_for_uid(cx, email_id.uid, ListFlags.NONE, cancellable);
|
||||
location = do_get_location_for_uid(cx, email_id.uid, ListFlags.INCLUDE_MARKED_FOR_REMOVE,
|
||||
cancellable);
|
||||
|
||||
if (location != null) {
|
||||
associated = true;
|
||||
|
|
@ -1056,17 +1079,18 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
}
|
||||
|
||||
Db.Statement search_stmt = cx.prepare(
|
||||
"SELECT ordering FROM MessageLocationTable WHERE message_id=? AND folder_id=?");
|
||||
"SELECT ordering, remove_marker FROM MessageLocationTable WHERE message_id=? AND folder_id=?");
|
||||
search_stmt.bind_rowid(0, message_id);
|
||||
search_stmt.bind_rowid(1, folder_id);
|
||||
|
||||
Db.Result search_results = search_stmt.exec(cancellable);
|
||||
if (!search_results.finished) {
|
||||
associated = true;
|
||||
location = new LocationIdentifier(message_id, new Imap.UID(search_results.int64_at(0)));
|
||||
location = new LocationIdentifier(message_id, new Imap.UID(search_results.int64_at(0)),
|
||||
search_results.bool_at(1));
|
||||
} else {
|
||||
assert(email_id.uid != null);
|
||||
location = new LocationIdentifier(message_id, email_id.uid);
|
||||
location = new LocationIdentifier(message_id, email_id.uid, false);
|
||||
}
|
||||
|
||||
return location;
|
||||
|
|
@ -1278,7 +1302,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
|
||||
private Geary.Email do_location_to_email(Db.Connection cx, LocationIdentifier location,
|
||||
Geary.Email.Field required_fields, ListFlags flags, Cancellable? cancellable) throws Error {
|
||||
if (!flags.include_marked_for_remove() && is_marked_removed(location.uid)) {
|
||||
if (!flags.include_marked_for_remove() && location.marked_removed) {
|
||||
throw new EngineError.NOT_FOUND("Message %s marked as removed in %s",
|
||||
location.email_id.to_string(), to_string());
|
||||
}
|
||||
|
|
@ -1871,7 +1895,8 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
update_stmt.exec(cancellable);
|
||||
}
|
||||
|
||||
// Db.Result must include columns for "message_id" and "ordering" from the MessageLocationTable
|
||||
// Db.Result must include columns for "message_id", "ordering", and "remove_marker" from the
|
||||
// MessageLocationTable
|
||||
private Gee.List<LocationIdentifier> do_results_to_locations(Db.Result results,
|
||||
ListFlags flags, Cancellable? cancellable) throws Error {
|
||||
Gee.List<LocationIdentifier> locations = new Gee.ArrayList<LocationIdentifier>();
|
||||
|
|
@ -1881,8 +1906,8 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
|
||||
do {
|
||||
LocationIdentifier location = new LocationIdentifier(results.rowid_for("message_id"),
|
||||
new Imap.UID(results.int64_for("ordering")));
|
||||
if (!flags.include_marked_for_remove() && is_marked_removed(location.uid))
|
||||
new Imap.UID(results.int64_for("ordering")), results.bool_for("remove_marker"));
|
||||
if (!flags.include_marked_for_remove() && location.marked_removed)
|
||||
continue;
|
||||
|
||||
locations.add(location);
|
||||
|
|
@ -1894,7 +1919,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
private LocationIdentifier? do_get_location_for_id(Db.Connection cx, ImapDB.EmailIdentifier id,
|
||||
ListFlags flags, Cancellable? cancellable) throws Error {
|
||||
Db.Statement stmt = cx.prepare("""
|
||||
SELECT ordering
|
||||
SELECT ordering, remove_marker
|
||||
FROM MessageLocationTable
|
||||
WHERE folder_id = ? AND message_id = ?
|
||||
""");
|
||||
|
|
@ -1905,20 +1930,16 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
if (result.finished)
|
||||
return null;
|
||||
|
||||
Imap.UID uid = new Imap.UID(result.int64_at(0));
|
||||
if (!flags.include_marked_for_remove() && is_marked_removed(uid))
|
||||
return null;
|
||||
LocationIdentifier location = new LocationIdentifier(id.message_id,
|
||||
new Imap.UID(result.int64_at(0)), result.bool_at(1));
|
||||
|
||||
return new LocationIdentifier(id.message_id, uid);
|
||||
return (!flags.include_marked_for_remove() && location.marked_removed) ? null : location;
|
||||
}
|
||||
|
||||
private LocationIdentifier? do_get_location_for_uid(Db.Connection cx, Imap.UID uid,
|
||||
ListFlags flags, Cancellable? cancellable) throws Error {
|
||||
if (!flags.include_marked_for_remove() && is_marked_removed(uid))
|
||||
return null;
|
||||
|
||||
Db.Statement stmt = cx.prepare("""
|
||||
SELECT message_id
|
||||
SELECT message_id, remove_marker
|
||||
FROM MessageLocationTable
|
||||
WHERE folder_id = ? AND ordering = ?
|
||||
""");
|
||||
|
|
@ -1929,7 +1950,9 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
|
|||
if (result.finished)
|
||||
return null;
|
||||
|
||||
return new LocationIdentifier(result.rowid_at(0), uid);
|
||||
LocationIdentifier location = new LocationIdentifier(result.rowid_at(0), uid, result.bool_at(1));
|
||||
|
||||
return (!flags.include_marked_for_remove() && location.marked_removed) ? null : location;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -157,12 +157,26 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
|
|||
assert(local_earliest_id.has_uid());
|
||||
assert(local_latest_id.has_uid());
|
||||
|
||||
// if any messages are still marked for removal from last time, that means the EXPUNGE
|
||||
// never arrived from the server, in which case the folder is "dirty" and needs a full
|
||||
// normalization
|
||||
int remove_markers = yield local_folder.get_marked_for_remove_count_async(cancellable);
|
||||
bool is_dirty = (remove_markers != 0);
|
||||
|
||||
if (is_dirty)
|
||||
debug("%s: %d remove markers found, folder is dirty", to_string(), remove_markers);
|
||||
|
||||
// if UIDNEXT has changed, that indicates messages have been appended (and possibly removed)
|
||||
int64 uidnext_diff = remote_properties.uid_next.value - local_properties.uid_next.value;
|
||||
|
||||
int local_message_count = (local_properties.select_examine_messages >= 0)
|
||||
? local_properties.select_examine_messages : 0;
|
||||
int remote_message_count = (remote_properties.select_examine_messages >= 0)
|
||||
? remote_properties.select_examine_messages : 0;
|
||||
|
||||
// if UIDNEXT is the same as last time AND the total count of email is the same, then
|
||||
// nothing has been added or removed
|
||||
if (uidnext_diff == 0 && local_properties.email_total == remote_properties.email_total) {
|
||||
if (!is_dirty && uidnext_diff == 0 && local_message_count == remote_message_count) {
|
||||
debug("%s: No messages added/removed since last opened, normalization completed", to_string());
|
||||
|
||||
return true;
|
||||
|
|
@ -180,7 +194,7 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
|
|||
// (Also, this cannot fail; if this situation exists, then it cannot by definition indicate another
|
||||
// situation, esp. messages being removed.)
|
||||
Imap.UID first_uid;
|
||||
if (uidnext_diff == (remote_properties.select_examine_messages - local_properties.select_examine_messages)) {
|
||||
if (!is_dirty && uidnext_diff == (remote_message_count - local_message_count)) {
|
||||
first_uid = local_latest_id.uid.next();
|
||||
|
||||
debug("%s: Messages only appended (local/remote UIDNEXT=%s/%s total=%d/%d diff=%s), gathering mail UIDs %s:%s",
|
||||
|
|
@ -199,7 +213,7 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
|
|||
// get all the UIDs in said range from the local store, sorted; convert to non-null
|
||||
// for ease of use later
|
||||
Gee.SortedSet<Imap.UID>? local_uids = yield local_folder.list_uids_by_range_async(
|
||||
first_uid, last_uid, cancellable);
|
||||
first_uid, last_uid, true, cancellable);
|
||||
if (local_uids == null)
|
||||
local_uids = new Gee.TreeSet<Imap.UID>();
|
||||
|
||||
|
|
@ -233,6 +247,9 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
|
|||
discovered_uids.add(remote_uid);
|
||||
}
|
||||
|
||||
debug("%s: changes since last seen: removed=%d appended=%d discovered=%d", to_string(),
|
||||
removed_uids.size, appended_uids.size, discovered_uids.size);
|
||||
|
||||
// fetch from the server the local store's required flags for all appended/inserted messages
|
||||
// (which is simply equal to all remaining remote UIDs)
|
||||
Gee.List<Geary.Email>? to_create = null;
|
||||
|
|
@ -277,12 +294,20 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
|
|||
// Convert removed UIDs into EmailIdentifiers and detach immediately
|
||||
Gee.Set<ImapDB.EmailIdentifier>? removed_ids = null;
|
||||
if (removed_uids.size > 0) {
|
||||
removed_ids = yield local_folder.get_ids_async(removed_uids, cancellable);
|
||||
yield local_folder.detach_multiple_emails_async(removed_ids, cancellable);
|
||||
removed_ids = yield local_folder.get_ids_async(removed_uids,
|
||||
ImapDB.Folder.ListFlags.INCLUDE_MARKED_FOR_REMOVE, cancellable);
|
||||
if (removed_ids != null && removed_ids.size > 0) {
|
||||
yield local_folder.detach_multiple_emails_async(removed_ids, cancellable);
|
||||
}
|
||||
}
|
||||
|
||||
check_open("normalize_folders (removed emails)");
|
||||
|
||||
// remove any extant remove markers, as everything is accounted for now
|
||||
yield local_folder.clear_remove_markers_async(cancellable);
|
||||
|
||||
check_open("normalize_folders (clear remove markers)");
|
||||
|
||||
//
|
||||
// now normalized
|
||||
// notify subscribers of changes
|
||||
|
|
@ -356,9 +381,6 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
|
|||
// start the replay queue
|
||||
replay_queue = new ReplayQueue(this);
|
||||
|
||||
// reset state in the local (database) folder
|
||||
local_folder.reset();
|
||||
|
||||
// do NOT open the remote side here; wait for the ReplayQueue to require a remote connection
|
||||
// or wait_for_open_async() to be called ... this allows for fast local-only operations
|
||||
// to occur, local-only either because (a) the folder has all the information required
|
||||
|
|
@ -539,9 +561,6 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
|
|||
// time, even if remote was not fully opened, as some callers rely on order of signals
|
||||
notify_closed(remote_reason);
|
||||
|
||||
// close local store (reset its state)
|
||||
local_folder.reset();
|
||||
|
||||
// see above note for why this must be called every time
|
||||
notify_closed(local_reason);
|
||||
|
||||
|
|
@ -921,7 +940,7 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
|
|||
check_ids("list_local_email_fields_async", ids);
|
||||
|
||||
return yield local_folder.list_email_fields_by_id_async(
|
||||
(Gee.Collection<Geary.ImapDB.EmailIdentifier>) ids, cancellable);
|
||||
(Gee.Collection<Geary.ImapDB.EmailIdentifier>) ids, ImapDB.Folder.ListFlags.NONE, cancellable);
|
||||
}
|
||||
|
||||
public override async Geary.Email fetch_email_async(Geary.EmailIdentifier id,
|
||||
|
|
|
|||
|
|
@ -196,6 +196,9 @@ private class Geary.ImapEngine.ReplayQueue : Geary.BaseObject {
|
|||
}
|
||||
|
||||
private bool on_notification_timeout() {
|
||||
if (notification_queue.size == 0)
|
||||
return false;
|
||||
|
||||
debug("%s: Scheduling %d held server notification operations", owner.to_string(),
|
||||
notification_queue.size);
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ private class Geary.ImapEngine.ServerSearchEmail : Geary.ImapEngine.AbstractList
|
|||
|
||||
// if the earliest UID is not in the local store, then need to expand vector to it
|
||||
Geary.EmailIdentifier? first_id = yield owner.local_folder.get_id_async(uids.first(),
|
||||
cancellable);
|
||||
ImapDB.Folder.ListFlags.NONE, cancellable);
|
||||
if (first_id == null)
|
||||
yield expand_vector_async(uids.first(), 1);
|
||||
|
||||
|
|
@ -45,7 +45,8 @@ private class Geary.ImapEngine.ServerSearchEmail : Geary.ImapEngine.AbstractList
|
|||
// to the database yet)
|
||||
//
|
||||
// TODO: We need a sparse version of this to scoop them up all at once
|
||||
ImapDB.EmailIdentifier? id = yield owner.local_folder.get_id_async(uid, cancellable);
|
||||
ImapDB.EmailIdentifier? id = yield owner.local_folder.get_id_async(uid,
|
||||
ImapDB.Folder.ListFlags.NONE, cancellable);
|
||||
if (id != null)
|
||||
local_ids.add(id);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue