From 8ccb3100c26c204710626a6b0e5af480e707e89a Mon Sep 17 00:00:00 2001 From: MarekWo Date: Wed, 15 Apr 2026 08:35:31 +0200 Subject: [PATCH] fix: cascade-clean ignored/blocked rows on hard contact delete hard_delete_contact() failed with FOREIGN KEY constraint when the contact had a row in ignored_contacts or blocked_contacts, since those FKs lacked ON DELETE CASCADE. Delete dependent rows first in the same transaction; also update schema for new deployments. Co-Authored-By: Claude Opus 4.6 --- app/database.py | 14 +++++++++----- app/schema.sql | 4 ++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/app/database.py b/app/database.py index 9b47797..a3ec77a 100644 --- a/app/database.py +++ b/app/database.py @@ -197,12 +197,16 @@ class Database: return cursor.rowcount > 0 def hard_delete_contact(self, public_key: str) -> bool: - """Permanently delete a contact from the database.""" + """Permanently delete a contact from the database. + + Also clears ignored/blocked rows that reference this contact + via FK without ON DELETE CASCADE. + """ + pk = public_key.lower() with self._connect() as conn: - cursor = conn.execute( - "DELETE FROM contacts WHERE public_key = ?", - (public_key.lower(),) - ) + conn.execute("DELETE FROM ignored_contacts WHERE public_key = ?", (pk,)) + conn.execute("DELETE FROM blocked_contacts WHERE public_key = ?", (pk,)) + cursor = conn.execute("DELETE FROM contacts WHERE public_key = ?", (pk,)) return cursor.rowcount > 0 def downgrade_stale_device_contacts(self, active_device_keys: set) -> int: diff --git a/app/schema.sql b/app/schema.sql index e4f5e50..d4203b1 100644 --- a/app/schema.sql +++ b/app/schema.sql @@ -152,13 +152,13 @@ CREATE TABLE IF NOT EXISTS read_status ( -- Ignored contacts (adverts cached but not pending/auto-added) CREATE TABLE IF NOT EXISTS ignored_contacts ( - public_key TEXT PRIMARY KEY REFERENCES contacts(public_key), + public_key TEXT PRIMARY KEY REFERENCES contacts(public_key) ON DELETE CASCADE, created_at TEXT NOT NULL DEFAULT (datetime('now')) ); -- Blocked contacts (ignored + messages hidden from display) CREATE TABLE IF NOT EXISTS blocked_contacts ( - public_key TEXT PRIMARY KEY REFERENCES contacts(public_key), + public_key TEXT PRIMARY KEY REFERENCES contacts(public_key) ON DELETE CASCADE, created_at TEXT NOT NULL DEFAULT (datetime('now')) );