From 303729f3d3adca556f2a5f0fb086957a9c5fb78d Mon Sep 17 00:00:00 2001 From: JonKazama-Hellion Date: Sat, 2 May 2026 21:25:05 +0200 Subject: [PATCH] refactor(db): parameterise DeleteByRetentionPolicy SQL clauses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per-channel WHERE tuples and the catch-all default-clause now bind ChatType and cutoff via named parameters instead of being inlined as literals. Combines BindIntList for the explicit-types exclusion with explicit AddWithValue for each (type, cutoff) tuple. Behavioural diff against v0.5.0: none — same retention windows, same cutoff math, just parameterised. --- ChatTwo/MessageStore.cs | 51 ++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/ChatTwo/MessageStore.cs b/ChatTwo/MessageStore.cs index 72b2623..4aa5904 100644 --- a/ChatTwo/MessageStore.cs +++ b/ChatTwo/MessageStore.cs @@ -346,31 +346,44 @@ internal class MessageStore : IDisposable throw new ArgumentOutOfRangeException(nameof(chatTypeDaysMap), "Negative retention is not allowed."); var nowMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); - var clauses = new List(); - foreach (var (type, days) in chatTypeDaysMap) - { - var cutoff = nowMs - days * 86400000L; - clauses.Add($"(ChatType = {type} AND Date < {cutoff})"); - } - // Catch-all for channels without an explicit override. "0" is treated - // as "do not delete by default" — without an explicit user override, - // unmapped channels stay forever instead of getting wiped immediately. - if (defaultDays > 0) - { - var cutoff = nowMs - defaultDays * 86400000L; - var explicitTypes = chatTypeDaysMap.Count > 0 - ? string.Join(",", chatTypeDaysMap.Keys) - : "-1"; // empty list would produce invalid SQL - clauses.Add($"(ChatType NOT IN ({explicitTypes}) AND Date < {cutoff})"); - } - - if (clauses.Count == 0) + if (chatTypeDaysMap.Count == 0 && defaultDays <= 0) return 0; long deleted; using (var cmd = Connection.CreateCommand()) { + var clauses = new List(); + var index = 0; + foreach (var (type, days) in chatTypeDaysMap) + { + var cutoff = nowMs - days * 86400000L; + var typeParam = $"$type{index}"; + var cutoffParam = $"$cutoff{index}"; + cmd.Parameters.AddWithValue(typeParam, type); + cmd.Parameters.AddWithValue(cutoffParam, cutoff); + clauses.Add($"(ChatType = {typeParam} AND Date < {cutoffParam})"); + index++; + } + + // Catch-all for channels without an explicit override. "0" is + // treated as "do not delete by default" — without an explicit + // user override, unmapped channels stay forever instead of + // getting wiped immediately. + if (defaultDays > 0) + { + var defaultCutoff = nowMs - defaultDays * 86400000L; + cmd.Parameters.AddWithValue("$defaultCutoff", defaultCutoff); + + var explicitPlaceholders = chatTypeDaysMap.Count > 0 + ? BindIntList(cmd, "explicit", chatTypeDaysMap.Keys) + : "-1"; // empty list would produce invalid SQL + clauses.Add($"(ChatType NOT IN ({explicitPlaceholders}) AND Date < $defaultCutoff)"); + } + + if (clauses.Count == 0) + return 0; + cmd.CommandText = $"DELETE FROM messages WHERE {string.Join(" OR ", clauses)};"; cmd.CommandTimeout = 600; deleted = cmd.ExecuteNonQuery();