From 3990c0ad7f0cd4cc2b03df73ada8ea3fd186364f Mon Sep 17 00:00:00 2001 From: Devathmaj Date: Sat, 15 Aug 2026 20:03:15 +0530 Subject: [PATCH] fix: repair voucher_posts view rebuild and add missing timestamps - DROP VIEW before recreating voucher_posts in h4d5e6f7a8b9 (Postgres CREATE OR REPLACE VIEW cannot reorder columns) - Add new migration adding created_at/updated_at to keywords and pipeline_lock, which Base declares but their create migrations omitted - Update expected migration head in tests Fixes the scheduler crash (UndefinedColumnError on keywords.created_at) and the broken migration chain. --- ...b9_add_vendor_mappings_and_posts_vendor.py | 8 ++- ...eated_updated_to_keywords_pipeline_lock.py | 51 +++++++++++++++++++ tests/test_migrations.py | 2 +- 3 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 migrations/versions/o9p8q7r6s5t4_add_created_updated_to_keywords_pipeline_lock.py diff --git a/migrations/versions/h4d5e6f7a8b9_add_vendor_mappings_and_posts_vendor.py b/migrations/versions/h4d5e6f7a8b9_add_vendor_mappings_and_posts_vendor.py index 11367e7..82cab63 100644 --- a/migrations/versions/h4d5e6f7a8b9_add_vendor_mappings_and_posts_vendor.py +++ b/migrations/versions/h4d5e6f7a8b9_add_vendor_mappings_and_posts_vendor.py @@ -67,10 +67,12 @@ def upgrade() -> None: op.create_index("ix_posts_vendor", "posts", ["vendor"], unique=False) # ── 3. Update voucher_posts view ────────────────────────────────────────── + # CREATE OR REPLACE VIEW cannot reorder view columns, so drop first. + op.execute(sa.text("DROP VIEW IF EXISTS voucher_posts")) op.execute( sa.text( """ - CREATE OR REPLACE VIEW voucher_posts AS + CREATE VIEW voucher_posts AS SELECT p.id, p.source_id, @@ -112,10 +114,12 @@ def upgrade() -> None: def downgrade() -> None: + # CREATE OR REPLACE VIEW cannot reorder view columns, so drop first. + op.execute(sa.text("DROP VIEW IF EXISTS voucher_posts")) op.execute( sa.text( """ - CREATE OR REPLACE VIEW voucher_posts AS + CREATE VIEW voucher_posts AS SELECT p.id, p.source_id, diff --git a/migrations/versions/o9p8q7r6s5t4_add_created_updated_to_keywords_pipeline_lock.py b/migrations/versions/o9p8q7r6s5t4_add_created_updated_to_keywords_pipeline_lock.py new file mode 100644 index 0000000..0e5b71c --- /dev/null +++ b/migrations/versions/o9p8q7r6s5t4_add_created_updated_to_keywords_pipeline_lock.py @@ -0,0 +1,51 @@ +"""Add missing created_at/updated_at to keywords and pipeline_lock + +Revision ID: o9p8q7r6s5t4 +Revises: m6n7o8p9q0r1 +Create Date: 2026-08-15 + +The ``Base`` model declares ``created_at``/``updated_at`` on every mapped +table, but the ``keywords`` (g3b9c0d1e2f3) and ``pipeline_lock`` +(e1c2d3a4b5f6) creation migrations omitted them. The ORM therefore emits +SELECTs referencing ``keywords.created_at`` / ``pipeline_lock.created_at`` +which never existed, failing the scheduler with ``UndefinedColumnError``. +This migration backfills the missing columns to match the models. +""" + +from typing import Sequence, Union + +import sqlalchemy as sa +from alembic import op + +revision: str = "o9p8q7r6s5t4" +down_revision: Union[str, Sequence[str], None] = "m6n7o8p9q0r1" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + for table in ("keywords", "pipeline_lock"): + op.add_column( + table, + sa.Column( + "created_at", + sa.DateTime(), + nullable=False, + server_default=sa.func.now(), + ), + ) + op.add_column( + table, + sa.Column( + "updated_at", + sa.DateTime(), + nullable=False, + server_default=sa.func.now(), + ), + ) + + +def downgrade() -> None: + for table in ("pipeline_lock", "keywords"): + op.drop_column(table, "updated_at") + op.drop_column(table, "created_at") diff --git a/tests/test_migrations.py b/tests/test_migrations.py index f84379b..2dd85dd 100644 --- a/tests/test_migrations.py +++ b/tests/test_migrations.py @@ -16,7 +16,7 @@ _VERSIONS_DIR = Path(__file__).resolve().parents[1] / "migrations" / "versions" -_EXPECTED_HEAD = "m6n7o8p9q0r1" +_EXPECTED_HEAD = "o9p8q7r6s5t4" # Every model-backed table (views are excluded — voucher_posts is created via # migrations as a view, not through Base.metadata.create_all).