From 79d3f45068b95d52b7c4a1f6cce9ad253cce750f Mon Sep 17 00:00:00 2001 From: Albrecht Degering Date: Mon, 20 Jul 2026 20:06:26 +0200 Subject: [PATCH] fix(mail): repair mailbox index schema drift --- .../5f708192abcd_mail_mailbox_index_repair.py | 67 +++++++++++++++++++ ...8192abcd_v019_mail_mailbox_index_repair.py | 67 +++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 src/govoplan_mail/backend/migrations/dev_versions/5f708192abcd_mail_mailbox_index_repair.py create mode 100644 src/govoplan_mail/backend/migrations/versions/5f708192abcd_v019_mail_mailbox_index_repair.py diff --git a/src/govoplan_mail/backend/migrations/dev_versions/5f708192abcd_mail_mailbox_index_repair.py b/src/govoplan_mail/backend/migrations/dev_versions/5f708192abcd_mail_mailbox_index_repair.py new file mode 100644 index 0000000..d747c85 --- /dev/null +++ b/src/govoplan_mail/backend/migrations/dev_versions/5f708192abcd_mail_mailbox_index_repair.py @@ -0,0 +1,67 @@ +"""repair mail mailbox index columns + +Revision ID: 5f708192abcd +Revises: 4e5f708192ab +Create Date: 2026-07-14 00:00:00.000000 +""" +from __future__ import annotations + +from alembic import op +import sqlalchemy as sa + + +revision = "5f708192abcd" +down_revision = "4e5f708192ab" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + inspector = sa.inspect(op.get_bind()) + tables = set(inspector.get_table_names()) + if "mail_mailbox_folder_index" in tables: + columns = _columns(inspector, "mail_mailbox_folder_index") + indexes = _indexes(inspector, "mail_mailbox_folder_index") + if "message_indexed_at" not in columns: + op.add_column("mail_mailbox_folder_index", sa.Column("message_indexed_at", sa.DateTime(timezone=True), nullable=True)) + if "ix_mail_mailbox_folder_index_message_indexed_at" not in indexes: + op.create_index(op.f("ix_mail_mailbox_folder_index_message_indexed_at"), "mail_mailbox_folder_index", ["message_indexed_at"], unique=False) + + if "mail_mailbox_message_index" in tables: + columns = _columns(inspector, "mail_mailbox_message_index") + indexes = _indexes(inspector, "mail_mailbox_message_index") + if "sort_position" not in columns: + op.add_column("mail_mailbox_message_index", sa.Column("sort_position", sa.BigInteger(), nullable=False, server_default="0")) + if op.get_bind().dialect.name != "sqlite": + op.alter_column("mail_mailbox_message_index", "sort_position", server_default=None) + if "ix_mail_mailbox_message_index_sort_position" not in indexes: + op.create_index(op.f("ix_mail_mailbox_message_index_sort_position"), "mail_mailbox_message_index", ["sort_position"], unique=False) + if "ix_mail_mailbox_message_index_page" not in indexes: + op.create_index("ix_mail_mailbox_message_index_page", "mail_mailbox_message_index", ["tenant_id", "profile_id", "folder", "sort_position"], unique=False) + + +def downgrade() -> None: + inspector = sa.inspect(op.get_bind()) + tables = set(inspector.get_table_names()) + if "mail_mailbox_message_index" in tables: + indexes = _indexes(inspector, "mail_mailbox_message_index") + if "ix_mail_mailbox_message_index_page" in indexes: + op.drop_index("ix_mail_mailbox_message_index_page", table_name="mail_mailbox_message_index") + if "ix_mail_mailbox_message_index_sort_position" in indexes: + op.drop_index(op.f("ix_mail_mailbox_message_index_sort_position"), table_name="mail_mailbox_message_index") + if "sort_position" in _columns(inspector, "mail_mailbox_message_index"): + op.drop_column("mail_mailbox_message_index", "sort_position") + if "mail_mailbox_folder_index" in tables: + indexes = _indexes(inspector, "mail_mailbox_folder_index") + if "ix_mail_mailbox_folder_index_message_indexed_at" in indexes: + op.drop_index(op.f("ix_mail_mailbox_folder_index_message_indexed_at"), table_name="mail_mailbox_folder_index") + if "message_indexed_at" in _columns(inspector, "mail_mailbox_folder_index"): + op.drop_column("mail_mailbox_folder_index", "message_indexed_at") + + +def _columns(inspector, table_name: str) -> set[str]: + return {column["name"] for column in inspector.get_columns(table_name)} + + +def _indexes(inspector, table_name: str) -> set[str]: + return {index["name"] for index in inspector.get_indexes(table_name)} diff --git a/src/govoplan_mail/backend/migrations/versions/5f708192abcd_v019_mail_mailbox_index_repair.py b/src/govoplan_mail/backend/migrations/versions/5f708192abcd_v019_mail_mailbox_index_repair.py new file mode 100644 index 0000000..d747c85 --- /dev/null +++ b/src/govoplan_mail/backend/migrations/versions/5f708192abcd_v019_mail_mailbox_index_repair.py @@ -0,0 +1,67 @@ +"""repair mail mailbox index columns + +Revision ID: 5f708192abcd +Revises: 4e5f708192ab +Create Date: 2026-07-14 00:00:00.000000 +""" +from __future__ import annotations + +from alembic import op +import sqlalchemy as sa + + +revision = "5f708192abcd" +down_revision = "4e5f708192ab" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + inspector = sa.inspect(op.get_bind()) + tables = set(inspector.get_table_names()) + if "mail_mailbox_folder_index" in tables: + columns = _columns(inspector, "mail_mailbox_folder_index") + indexes = _indexes(inspector, "mail_mailbox_folder_index") + if "message_indexed_at" not in columns: + op.add_column("mail_mailbox_folder_index", sa.Column("message_indexed_at", sa.DateTime(timezone=True), nullable=True)) + if "ix_mail_mailbox_folder_index_message_indexed_at" not in indexes: + op.create_index(op.f("ix_mail_mailbox_folder_index_message_indexed_at"), "mail_mailbox_folder_index", ["message_indexed_at"], unique=False) + + if "mail_mailbox_message_index" in tables: + columns = _columns(inspector, "mail_mailbox_message_index") + indexes = _indexes(inspector, "mail_mailbox_message_index") + if "sort_position" not in columns: + op.add_column("mail_mailbox_message_index", sa.Column("sort_position", sa.BigInteger(), nullable=False, server_default="0")) + if op.get_bind().dialect.name != "sqlite": + op.alter_column("mail_mailbox_message_index", "sort_position", server_default=None) + if "ix_mail_mailbox_message_index_sort_position" not in indexes: + op.create_index(op.f("ix_mail_mailbox_message_index_sort_position"), "mail_mailbox_message_index", ["sort_position"], unique=False) + if "ix_mail_mailbox_message_index_page" not in indexes: + op.create_index("ix_mail_mailbox_message_index_page", "mail_mailbox_message_index", ["tenant_id", "profile_id", "folder", "sort_position"], unique=False) + + +def downgrade() -> None: + inspector = sa.inspect(op.get_bind()) + tables = set(inspector.get_table_names()) + if "mail_mailbox_message_index" in tables: + indexes = _indexes(inspector, "mail_mailbox_message_index") + if "ix_mail_mailbox_message_index_page" in indexes: + op.drop_index("ix_mail_mailbox_message_index_page", table_name="mail_mailbox_message_index") + if "ix_mail_mailbox_message_index_sort_position" in indexes: + op.drop_index(op.f("ix_mail_mailbox_message_index_sort_position"), table_name="mail_mailbox_message_index") + if "sort_position" in _columns(inspector, "mail_mailbox_message_index"): + op.drop_column("mail_mailbox_message_index", "sort_position") + if "mail_mailbox_folder_index" in tables: + indexes = _indexes(inspector, "mail_mailbox_folder_index") + if "ix_mail_mailbox_folder_index_message_indexed_at" in indexes: + op.drop_index(op.f("ix_mail_mailbox_folder_index_message_indexed_at"), table_name="mail_mailbox_folder_index") + if "message_indexed_at" in _columns(inspector, "mail_mailbox_folder_index"): + op.drop_column("mail_mailbox_folder_index", "message_indexed_at") + + +def _columns(inspector, table_name: str) -> set[str]: + return {column["name"] for column in inspector.get_columns(table_name)} + + +def _indexes(inspector, table_name: str) -> set[str]: + return {index["name"] for index in inspector.get_indexes(table_name)}