Files
webmail-client/backend/migrations/versions/0018_repair_thread_counters.py
T

56 lines
1.9 KiB
Python
Raw Normal View History

"""repair thread counters left stale by server-side flag changes and expunges
`threads.unread_count`, `pinned` and `message_count` are denormalized from the
thread's messages. Two sync paths updated messages without recomputing them: a
flag change reported by the server (mail read on a phone, or filed by a
server-side rule) and a QRESYNC expunge. The row then disagreed with its own
messages — the message list showing a message as unread that the reading pane
showed as read, and only a manual "mark as read" reconciling it.
The code paths are fixed; this repairs rows that already drifted. Recomputes from
the messages themselves, so it is safe to run on a healthy database: threads that
already agree are left untouched.
Revision ID: 0018
Revises: 0017
Create Date: 2026-09-18
"""
from __future__ import annotations
from collections.abc import Sequence
from alembic import op
revision: str = "0018"
down_revision: str | None = "0017"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
op.execute(
r"""
UPDATE threads t
SET unread_count = s.unread,
pinned = s.pinned,
message_count = s.total
FROM (
SELECT thread_id,
count(*) AS total,
count(*) FILTER (WHERE NOT ('\Seen' = ANY(flags))) AS unread,
bool_or('\Flagged' = ANY(flags)) AS pinned
FROM messages
WHERE thread_id IS NOT NULL
GROUP BY thread_id
) AS s
WHERE t.id = s.thread_id
AND (t.unread_count, t.pinned, t.message_count) IS DISTINCT FROM
(s.unread, s.pinned, s.total)
"""
)
def downgrade() -> None:
"""Nothing to undo: this only makes rows agree with the messages they describe."""