"""Add last_seen_at and reason_closed to terminal_sessions Revision ID: 0015 Revises: 0014 Create Date: 2024-12-18 """ from typing import Sequence, Union from alembic import op import sqlalchemy as sa # revision identifiers, used by Alembic. revision: str = '0015' down_revision: Union[str, None] = '0014' branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: # NOTE: SQLite cannot do `ALTER TABLE ... ADD COLUMN ... DEFAULT CURRENT_TIMESTAMP`. # We use batch mode to recreate the table safely. with op.batch_alter_table('terminal_sessions', recreate='always') as batch_op: batch_op.add_column(sa.Column('last_seen_at', sa.DateTime(timezone=True), nullable=True)) batch_op.add_column(sa.Column('reason_closed', sa.String(30), nullable=True)) # Backfill last_seen_at from created_at for existing rows op.execute("UPDATE terminal_sessions SET last_seen_at = created_at WHERE last_seen_at IS NULL") # Enforce NOT NULL + default for future inserts (recreate table again for SQLite) with op.batch_alter_table('terminal_sessions', recreate='always') as batch_op: batch_op.alter_column('last_seen_at', nullable=False, server_default=sa.text('CURRENT_TIMESTAMP')) # Create indexes for efficient queries op.create_index('ix_terminal_sessions_user_status', 'terminal_sessions', ['user_id', 'status']) op.create_index('ix_terminal_sessions_last_seen', 'terminal_sessions', ['last_seen_at']) def downgrade() -> None: # Drop indexes op.drop_index('ix_terminal_sessions_last_seen', table_name='terminal_sessions') op.drop_index('ix_terminal_sessions_user_status', table_name='terminal_sessions') # Drop columns (batch mode for SQLite) with op.batch_alter_table('terminal_sessions', recreate='always') as batch_op: batch_op.drop_column('reason_closed') batch_op.drop_column('last_seen_at')