From eb31d09782e92d8d04c2f221eb13e2741424212a Mon Sep 17 00:00:00 2001 From: Giorgio Gilestro Date: Wed, 27 May 2026 19:35:09 +0200 Subject: [PATCH] =?UTF-8?q?alembic:=200023=20=E2=80=94=20users.lang=20inde?= =?UTF-8?q?x=20+=20widen=20quotes=5Fdaily.symbol?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two related schema fixes from the code review: - users.lang gets a single-column index. The ai_log_job and email_digest_job both SELECT DISTINCT on this column every cycle; even at low cardinality an index is the right shape. - quotes_daily.symbol widened to VARCHAR(128) to match quotes.symbol (widened back in 0005). Long Eurostat/ONS symbols would silently truncate during rollup otherwise. Models updated to match (User.lang gains index=True, QuoteDaily.symbol goes to String(128)). Co-Authored-By: Claude Opus 4.7 --- .../0023_lang_index_and_qd_symbol_widen.py | 38 +++++++++++++++++++ app/models.py | 3 +- 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 alembic/versions/0023_lang_index_and_qd_symbol_widen.py diff --git a/alembic/versions/0023_lang_index_and_qd_symbol_widen.py b/alembic/versions/0023_lang_index_and_qd_symbol_widen.py new file mode 100644 index 0000000..42bcb63 --- /dev/null +++ b/alembic/versions/0023_lang_index_and_qd_symbol_widen.py @@ -0,0 +1,38 @@ +"""users.lang index + widen quotes_daily.symbol to VARCHAR(128). + +Revision ID: 0023 +Revises: 0022 +Create Date: 2026-05-27 +""" +from typing import Sequence, Union + +import sqlalchemy as sa +from alembic import op + + +revision: str = "0023" +down_revision: Union[str, None] = "0022" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + op.create_index("ix_users_lang", "users", ["lang"]) + op.alter_column( + "quotes_daily", + "symbol", + existing_type=sa.String(length=64), + type_=sa.String(length=128), + existing_nullable=False, + ) + + +def downgrade() -> None: + op.alter_column( + "quotes_daily", + "symbol", + existing_type=sa.String(length=128), + type_=sa.String(length=64), + existing_nullable=False, + ) + op.drop_index("ix_users_lang", table_name="users") diff --git a/app/models.py b/app/models.py index a16e039..7b0a2ef 100644 --- a/app/models.py +++ b/app/models.py @@ -59,7 +59,7 @@ class Quote(Base): class QuoteDaily(Base): """Daily rollup — sparkline source. PK on (symbol, date).""" __tablename__ = "quotes_daily" - symbol: Mapped[str] = mapped_column(String(64), primary_key=True) + symbol: Mapped[str] = mapped_column(String(128), primary_key=True) date: Mapped[date] = mapped_column(Date, primary_key=True) close: Mapped[float | None] = mapped_column(Float) high: Mapped[float | None] = mapped_column(Float) @@ -228,6 +228,7 @@ class User(Base): # app/services/i18n.py before writing. lang: Mapped[str] = mapped_column( String(8), nullable=False, default="en", server_default="en", + index=True, ) # Polar (MoR) linkage — populated by the polar_webhook handler the # first time we see a subscription/order event for the user. The