@@ -51,6 +51,7 @@ public final class SQLiteDatabase: @unchecked Sendable {
5151 try execute ( " PRAGMA journal_mode=WAL; " )
5252 try execute ( " PRAGMA foreign_keys=ON; " )
5353 try migrate ( )
54+ try compactLegacyActivityEventContentHashes ( )
5455 try seedDefaultsIfNeeded ( )
5556 }
5657
@@ -292,7 +293,8 @@ public final class SQLiteDatabase: @unchecked Sendable {
292293 }
293294
294295 public func saveActivityEvent( _ event: ActivityEvent ) throws -> ActivityEvent {
295- try withImmediateTransaction {
296+ let normalizedEvent = normalizedActivityEvent ( event)
297+ return try withImmediateTransaction {
296298 let overlappingDuplicates = try query (
297299 """
298300 SELECT * FROM activity_events
@@ -303,14 +305,14 @@ public final class SQLiteDatabase: @unchecked Sendable {
303305 ORDER BY started_at ASC, ended_at DESC;
304306 """ ,
305307 bindings: [
306- . text( event . contentHash) ,
307- . integer( event . isExcluded ? 1 : 0 ) ,
308- . double( event . endedAt. timeIntervalSince1970) ,
309- . double( event . startedAt. timeIntervalSince1970) ,
308+ . text( normalizedEvent . contentHash) ,
309+ . integer( normalizedEvent . isExcluded ? 1 : 0 ) ,
310+ . double( normalizedEvent . endedAt. timeIntervalSince1970) ,
311+ . double( normalizedEvent . startedAt. timeIntervalSince1970) ,
310312 ]
311313 ) . map ( ActivityEvent . init ( row: ) )
312314
313- let mergedEvent = mergeActivityEvent ( event , with: overlappingDuplicates)
315+ let mergedEvent = mergeActivityEvent ( normalizedEvent , with: overlappingDuplicates)
314316 for duplicate in overlappingDuplicates where duplicate. id != mergedEvent. id {
315317 try execute ( " DELETE FROM activity_events_fts WHERE id = ?; " , bindings: [ . text( duplicate. id) ] )
316318 try execute ( " DELETE FROM activity_events WHERE id = ?; " , bindings: [ . text( duplicate. id) ] )
@@ -723,6 +725,45 @@ public final class SQLiteDatabase: @unchecked Sendable {
723725 }
724726 }
725727
728+ private func compactLegacyActivityEventContentHashes( batchSize: Int = 500 ) throws {
729+ while true {
730+ let rows = try query (
731+ """
732+ SELECT id, bundle_id, window_title, url, visible_text
733+ FROM activity_events
734+ WHERE LENGTH(content_hash) > ?
735+ LIMIT ?;
736+ """ ,
737+ bindings: [
738+ . integer( Int64 ( ActivityEventContentHash . oversizedLegacyThreshold) ) ,
739+ . integer( Int64 ( batchSize) ) ,
740+ ]
741+ )
742+
743+ guard rows. isEmpty == false else {
744+ return
745+ }
746+
747+ try withImmediateTransaction {
748+ for row in rows {
749+ let compactHash = ActivityEventContentHash . make (
750+ bundleId: row. stringValue ( for: " bundle_id " ) ,
751+ windowTitle: row. stringValue ( for: " window_title " ) ,
752+ url: row. optionalStringValue ( for: " url " ) ,
753+ visibleText: row. stringValue ( for: " visible_text " )
754+ )
755+ try execute (
756+ " UPDATE activity_events SET content_hash = ? WHERE id = ?; " ,
757+ bindings: [
758+ . text( compactHash) ,
759+ . text( row. stringValue ( for: " id " ) ) ,
760+ ]
761+ )
762+ }
763+ }
764+ }
765+ }
766+
726767 private func seedDefaultsIfNeeded( ) throws {
727768 let countRows = try query ( " SELECT COUNT(*) AS value FROM provider_configs; " )
728769 let count = countRows. first? . intValue ( for: " value " ) ?? 0
@@ -781,6 +822,23 @@ public final class SQLiteDatabase: @unchecked Sendable {
781822 return value
782823 }
783824
825+ private func normalizedActivityEvent( _ event: ActivityEvent ) -> ActivityEvent {
826+ let compactHash = ActivityEventContentHash . compactIfNeeded (
827+ event. contentHash,
828+ bundleId: event. bundleId,
829+ windowTitle: event. windowTitle,
830+ url: event. url,
831+ visibleText: event. visibleText
832+ )
833+ guard compactHash != event. contentHash else {
834+ return event
835+ }
836+
837+ var normalized = event
838+ normalized. contentHash = compactHash
839+ return normalized
840+ }
841+
784842 private func mergeActivityEvent( _ event: ActivityEvent , with duplicates: [ ActivityEvent ] ) -> ActivityEvent {
785843 guard let canonical = duplicates. first else {
786844 return event
0 commit comments