0001 called Base.metadata.create_all(), building the schema from the current models, so on a fresh PostgreSQL 0002 collided on datasets.raw_sha256 and `alembic upgrade head` could never run from zero. 0001 is now an explicit, frozen baseline: the seven base tables exactly as the real history created them, before 0002/0006 extended them. Databases already past 0001 never re-run it. Proof: a fresh `upgrade head` produces a schema identical (pg_dump --schema-only) to the long-lived dev database. 0019 repairs a latent bug found by that comparison: 0006 used server_default="now()" as a string, which PostgreSQL froze at the instant the column was created, so every Agent and Job got the same created_at. It also adds three indexes the models declare but no migration created. central/tests/test_migration_baseline.py guards (via AST) against migrations importing the models or using string timestamp defaults. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
44 lines
1.7 KiB
Python
44 lines
1.7 KiB
Python
"""Fix frozen timestamp defaults from 0006 and add model indexes never migrated.
|
|
|
|
0006 declared ``server_default="now()"`` as a plain string. SQLAlchemy renders
|
|
that as the literal ``'now()'``, which PostgreSQL casts to a timestamp once,
|
|
when the column is created — every later row got that same fixed instant
|
|
instead of the insert time (agents.created_at/updated_at, jobs.created_at).
|
|
The models rely on the server default, so Jobs and Agents registered after
|
|
0006 all carried the migration's timestamp. Existing rows are left as they
|
|
are: their real creation time is not recoverable.
|
|
|
|
The models also declare ``index=True`` on three columns that no migration
|
|
ever indexed.
|
|
|
|
Revision ID: 0019_fix_timestamp_defaults
|
|
Revises: 0018_blocker_installation
|
|
"""
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
|
|
revision = "0019_fix_timestamp_defaults"
|
|
down_revision = "0018_blocker_installation"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
_TIMESTAMP_COLUMNS = (("agents", "created_at"), ("agents", "updated_at"), ("jobs", "created_at"))
|
|
_MISSING_INDEXES = (
|
|
("ix_kb_observations_tenant_id", "kb_observations", "tenant_id"),
|
|
("ix_kb_observations_company_key", "kb_observations", "company_key"),
|
|
("ix_tenant_product_context_company_key", "tenant_product_context", "company_key"),
|
|
)
|
|
|
|
|
|
def upgrade():
|
|
for table, column in _TIMESTAMP_COLUMNS:
|
|
op.alter_column(table, column, server_default=sa.func.now(), existing_type=sa.DateTime(timezone=True), existing_nullable=False)
|
|
for name, table, column in _MISSING_INDEXES:
|
|
op.create_index(name, table, [column])
|
|
|
|
|
|
def downgrade():
|
|
# The frozen-timestamp defaults are a bug and are deliberately not restored.
|
|
for name, table, _column in reversed(_MISSING_INDEXES):
|
|
op.drop_index(name, table_name=table)
|