|
8 | 8 | "encoding/json"
|
9 | 9 | "errors"
|
10 | 10 | "hash/fnv"
|
| 11 | + "sort" |
11 | 12 | "strconv"
|
12 | 13 | "strings"
|
13 | 14 | "time"
|
@@ -2257,78 +2258,117 @@ func (a *adapter) messagesHardDelete(topic string) error {
|
2257 | 2258 | return err
|
2258 | 2259 | }
|
2259 | 2260 |
|
| 2261 | +func rangeToQuery(delRanges []t.Range, topic string, query rdb.Term) rdb.Term { |
| 2262 | + if len(delRanges) > 1 || delRanges[0].Hi <= delRanges[0].Low { |
| 2263 | + var indexVals []any |
| 2264 | + for _, rng := range delRanges { |
| 2265 | + if rng.Hi == 0 { |
| 2266 | + indexVals = append(indexVals, []any{topic, rng.Low}) |
| 2267 | + } else { |
| 2268 | + for i := rng.Low; i <= rng.Hi; i++ { |
| 2269 | + indexVals = append(indexVals, []any{topic, i}) |
| 2270 | + } |
| 2271 | + } |
| 2272 | + } |
| 2273 | + query = query.GetAllByIndex("Topic_SeqId", indexVals...) |
| 2274 | + } else { |
| 2275 | + // Optimizing for a special case of single range low..hi |
| 2276 | + query = query.Between( |
| 2277 | + []any{topic, delRanges[0].Low}, |
| 2278 | + []any{topic, delRanges[0].Hi}, |
| 2279 | + rdb.BetweenOpts{Index: "Topic_SeqId", RightBound: "closed"}) |
| 2280 | + } |
| 2281 | + return query |
| 2282 | +} |
| 2283 | + |
2260 | 2284 | // MessageDeleteList deletes messages in the given topic with seqIds from the list.
|
2261 | 2285 | func (a *adapter) MessageDeleteList(topic string, toDel *t.DelMessage) error {
|
2262 |
| - var indexVals []any |
2263 | 2286 | var err error
|
2264 | 2287 |
|
2265 | 2288 | if toDel == nil {
|
2266 | 2289 | // Delete all messages.
|
2267 |
| - err = a.messagesHardDelete(topic) |
2268 |
| - } else { |
2269 |
| - // Only some messages are being deleted |
| 2290 | + return a.messagesHardDelete(topic) |
| 2291 | + } |
| 2292 | + |
| 2293 | + // Only some messages are being deleted |
2270 | 2294 |
|
2271 |
| - // Start with making a log entry |
2272 |
| - _, err = rdb.DB(a.dbName).Table("dellog").Insert(toDel).RunWrite(a.conn) |
| 2295 | + delRanges := toDel.SeqIdRanges |
| 2296 | + |
| 2297 | + if toDel.DeletedFor == "" { |
| 2298 | + // Hard-deleting messages requires updates to the messages table. |
| 2299 | + query := rangeToQuery(delRanges, topic, rdb.DB(a.dbName).Table("messages")) |
| 2300 | + |
| 2301 | + // Skip already hard-deleted messages. |
| 2302 | + query = query.Filter(rdb.Row.HasFields("DelId").Not()) |
| 2303 | + |
| 2304 | + // We are asked to delete messages no older than newerThan. |
| 2305 | + if newerThan := toDel.GetNewerThan(); newerThan != nil { |
| 2306 | + query = query.Filter(rdb.Row.Field("CreatedAt").Gt(newerThan)) |
| 2307 | + } |
| 2308 | + |
| 2309 | + query = query.Field("SeqId") |
| 2310 | + |
| 2311 | + // Find the actual IDs still present in the database. |
| 2312 | + cursor, err := query.Run(a.conn) |
2273 | 2313 | if err != nil {
|
2274 | 2314 | return err
|
2275 | 2315 | }
|
| 2316 | + defer cursor.Close() |
2276 | 2317 |
|
2277 |
| - query := rdb.DB(a.dbName).Table("messages") |
2278 |
| - if len(toDel.SeqIdRanges) > 1 || toDel.SeqIdRanges[0].Hi <= toDel.SeqIdRanges[0].Low { |
2279 |
| - for _, rng := range toDel.SeqIdRanges { |
2280 |
| - if rng.Hi == 0 { |
2281 |
| - indexVals = append(indexVals, []any{topic, rng.Low}) |
2282 |
| - } else { |
2283 |
| - for i := rng.Low; i <= rng.Hi; i++ { |
2284 |
| - indexVals = append(indexVals, []any{topic, i}) |
2285 |
| - } |
2286 |
| - } |
2287 |
| - } |
2288 |
| - query = query.GetAllByIndex("Topic_SeqId", indexVals...) |
2289 |
| - } else { |
2290 |
| - // Optimizing for a special case of single range low..hi |
2291 |
| - query = query.Between( |
2292 |
| - []any{topic, toDel.SeqIdRanges[0].Low}, |
2293 |
| - []any{topic, toDel.SeqIdRanges[0].Hi}, |
2294 |
| - rdb.BetweenOpts{Index: "Topic_SeqId", RightBound: "closed"}) |
| 2318 | + var seqIDs []int |
| 2319 | + if err = cursor.All(&seqIDs); err != nil { |
| 2320 | + return err |
2295 | 2321 | }
|
2296 |
| - // Skip already hard-deleted messages. |
2297 |
| - query = query.Filter(rdb.Row.HasFields("DelId").Not()) |
2298 |
| - if toDel.DeletedFor == "" { |
2299 |
| - // First decrement use counter for attachments. |
2300 |
| - if err = a.decFileUseCounter(query); err == nil { |
2301 |
| - // Hard-delete individual messages. Message is not deleted but all fields with personal content |
2302 |
| - // are removed. |
2303 |
| - _, err = query.Replace(rdb.Row.Without("Head", "From", "Content", "Attachments").Merge( |
2304 |
| - map[string]any{ |
2305 |
| - "DeletedAt": t.TimeNow(), "DelId": toDel.DelId})). |
2306 |
| - RunWrite(a.conn) |
2307 |
| - } |
2308 | 2322 |
|
2309 |
| - } else { |
2310 |
| - // Soft-deleting: adding DelId to DeletedFor |
2311 |
| - _, err = query. |
2312 |
| - // Skip messages already soft-deleted for the current user |
2313 |
| - Filter(func(row rdb.Term) any { |
2314 |
| - return rdb.Not(row.Field("DeletedFor").Default([]any{}).Contains( |
2315 |
| - func(df rdb.Term) any { |
2316 |
| - return df.Field("User").Eq(toDel.DeletedFor) |
2317 |
| - })) |
2318 |
| - }).Update(map[string]any{"DeletedFor": rdb.Row.Field("DeletedFor"). |
2319 |
| - Default([]any{}).Append( |
2320 |
| - &t.SoftDelete{ |
2321 |
| - User: toDel.DeletedFor, |
2322 |
| - DelId: toDel.DelId})}).RunWrite(a.conn) |
2323 |
| - } |
2324 |
| - |
2325 |
| - // If operation has failed, remove dellog record. |
| 2323 | + if len(seqIDs) == 0 { |
| 2324 | + // Nothing to delete. No need to make a log entry. All done. |
| 2325 | + return nil |
| 2326 | + } |
| 2327 | + |
| 2328 | + // Recalculate the actual ranges to delete. |
| 2329 | + sort.Ints(seqIDs) |
| 2330 | + delRanges = t.SliceToRanges(seqIDs) |
| 2331 | + |
| 2332 | + // Compose a new query with the new ranges. |
| 2333 | + query = rangeToQuery(delRanges, topic, rdb.DB(a.dbName).Table("messages")) |
| 2334 | + |
| 2335 | + // First decrement use counter for attachments. |
| 2336 | + if err = a.decFileUseCounter(query); err != nil { |
| 2337 | + return err |
| 2338 | + } |
| 2339 | + |
| 2340 | + // Hard-delete individual messages. The messages are not deleted but all fields with personal content |
| 2341 | + // are removed. |
| 2342 | + if _, err = query.Replace(rdb.Row.Without("Head", "From", "Content", "Attachments").Merge( |
| 2343 | + map[string]any{ |
| 2344 | + "DeletedAt": t.TimeNow(), "DelId": toDel.DelId})). |
| 2345 | + RunWrite(a.conn); err != nil { |
| 2346 | + return err |
| 2347 | + } |
| 2348 | + |
| 2349 | + } else { |
| 2350 | + // Soft-deleting: adding DelId to DeletedFor |
| 2351 | + _, err = rdb.DB(a.dbName).Table("messages"). |
| 2352 | + // Skip hard-deleted messages. |
| 2353 | + Filter(rdb.Row.HasFields("DelId").Not()). |
| 2354 | + // Skip messages already soft-deleted for the current user |
| 2355 | + Filter(func(row rdb.Term) any { |
| 2356 | + return rdb.Not(row.Field("DeletedFor").Default([]any{}).Contains( |
| 2357 | + func(df rdb.Term) any { |
| 2358 | + return df.Field("User").Eq(toDel.DeletedFor) |
| 2359 | + })) |
| 2360 | + }).Update(map[string]any{"DeletedFor": rdb.Row.Field("DeletedFor"). |
| 2361 | + Default([]any{}).Append( |
| 2362 | + &t.SoftDelete{ |
| 2363 | + User: toDel.DeletedFor, |
| 2364 | + DelId: toDel.DelId})}).RunWrite(a.conn) |
2326 | 2365 | if err != nil {
|
2327 |
| - rdb.DB(a.dbName).Table("dellog").Get(toDel.Id). |
2328 |
| - Delete(rdb.DeleteOpts{Durability: "soft", ReturnChanges: false}).RunWrite(a.conn) |
| 2366 | + return err |
2329 | 2367 | }
|
2330 | 2368 | }
|
2331 | 2369 |
|
| 2370 | + // Make log entries. Needed for both hard- and soft-deleting. |
| 2371 | + _, err = rdb.DB(a.dbName).Table("dellog").Insert(toDel).RunWrite(a.conn) |
2332 | 2372 | return err
|
2333 | 2373 | }
|
2334 | 2374 |
|
|
0 commit comments