diff --git a/HellionChat/MessageStore.cs b/HellionChat/MessageStore.cs index c46f841..b77701e 100644 --- a/HellionChat/MessageStore.cs +++ b/HellionChat/MessageStore.cs @@ -464,11 +464,35 @@ internal class MessageStore : IDisposable cmd.ExecuteNonQuery(); } + // Drops the full-text index and marks it for a rebuild. + // + // messages_fts stores sender_text and content_text in the clear, and no + // delete path touched it: ClearMessages, CleanupRetainOnly and the retention + // sweep all removed rows from `messages` only. The plain text of every + // "deleted" message stayed on disk. + // + // Worse, it was self-sealing. InitFtsReadyCache treats a non-empty index as + // ready, so after a wipe the index stayed full, the readiness flag stayed + // true, and the rebuild that would have cleared it never ran again. + // + // Wiping rather than deleting matched rows: message_guid is stored as a GUID + // string while messages.Id is a BLOB, so the two cannot be joined in SQL. + // The index is derived data and rebuilds from the surviving rows on the next + // start, which is the cheap and provably complete option. + // + // Caller must already hold _readLock. + private void InvalidateFtsIndex() + { + Connection.Execute("DELETE FROM messages_fts;"); + _ftsReady = false; + } + internal void ClearMessages() { lock (_readLock) { Connection.Execute("DELETE FROM messages;"); + InvalidateFtsIndex(); PerformMaintenance(); } } @@ -568,7 +592,11 @@ internal class MessageStore : IDisposable } if (deleted > 0) + { + InvalidateFtsIndex(); PerformMaintenance(); + } + return deleted; } } @@ -592,7 +620,16 @@ internal class MessageStore : IDisposable cmd.CommandTimeout = 600; deleted = cmd.ExecuteNonQuery(); } - PerformMaintenance(); + + // Skipped when nothing matched: VACUUM rewrites the whole file, and + // running it for zero deleted rows costs seconds on a large database + // for no benefit. DeleteByRetentionPolicy already guards this way. + if (deleted > 0) + { + InvalidateFtsIndex(); + PerformMaintenance(); + } + return deleted; } }