"""数据库迁移启动桥接层。""" from typing import Optional from sqlalchemy.engine import Engine from src.common.logger import get_logger from .builtin import ( LATEST_SCHEMA_VERSION, build_default_migration_registry, build_default_schema_version_resolver, ) from .exceptions import DatabaseMigrationExecutionError from .manager import DatabaseMigrationManager from .models import DatabaseMigrationState, MigrationPlan, ResolvedSchemaVersion, SchemaVersionSource from .registry import MigrationRegistry from .resolver import SchemaVersionResolver from .version_store import SQLiteUserVersionStore logger = get_logger("database_migration") class DatabaseMigrationBootstrapper: """数据库迁移启动桥接器。 该桥接器负责把数据库迁移基础设施接入现有启动流程,同时保持如下约束: 1. 若数据库为空,则直接交给当前模型定义建出最新结构; 2. 若数据库版本高于当前代码支持的最新版本,则立即终止启动; 3. 若存在待执行迁移步骤,则在正常建表流程之前先执行迁移; 4. 若数据库已是最新结构但尚未写入 ``user_version``,则在建表后补写版本号。 """ def __init__( self, manager: DatabaseMigrationManager, latest_schema_version: int = LATEST_SCHEMA_VERSION, ) -> None: """初始化数据库迁移启动桥接器。 Args: manager: 数据库迁移编排器。 latest_schema_version: 当前代码支持的最新 schema 版本号。 """ self.manager = manager self.latest_schema_version = latest_schema_version def prepare_database(self) -> DatabaseMigrationState: """为数据库初始化阶段准备迁移状态。 Returns: DatabaseMigrationState: 迁移准备完成后的数据库状态。 Raises: DatabaseMigrationExecutionError: 当数据库版本高于当前代码支持版本时抛出。 """ with self.manager.engine.connect() as connection: resolved_version = self.manager.resolver.resolve(connection) if resolved_version.version > self.latest_schema_version: raise DatabaseMigrationExecutionError( "当前数据库版本高于代码内注册的最新迁移版本,已拒绝继续启动。" f" 数据库版本={resolved_version.version},代码支持版本={self.latest_schema_version}" ) if resolved_version.source == SchemaVersionSource.EMPTY_DATABASE: logger.info( "检测到空数据库,将直接根据当前模型创建最新结构。" f" 目标版本={self.latest_schema_version}" ) return self._build_noop_state( current_version=resolved_version.version, target_version=self.latest_schema_version, resolved_state=resolved_version, ) migration_state = self.manager.describe_state(target_version=self.latest_schema_version) if not migration_state.requires_migration(): logger.info( f"数据库 schema 已是目标版本,无需迁移。当前版本={migration_state.resolved_version.version}" ) return migration_state logger.info( "检测到数据库需要迁移," f" 当前版本={migration_state.resolved_version.version},目标版本={migration_state.target_version}" ) self.manager.migrate(target_version=self.latest_schema_version) return self.manager.describe_state(target_version=self.latest_schema_version) def finalize_database(self, migration_state: DatabaseMigrationState) -> None: """在数据库初始化末尾补写最终 schema 版本号。 该方法主要负责两类场景: 1. 空库首次建表完成后,将 ``user_version`` 写入为最新版本; 2. 已是最新结构但此前未写入 ``user_version`` 的数据库,补写版本号。 Args: migration_state: 初始化前解析得到的迁移状态。 """ if migration_state.requires_migration(): return if migration_state.target_version <= 0: return if migration_state.resolved_version.source == SchemaVersionSource.PRAGMA: return with self.manager.engine.begin() as connection: self.manager.version_store.write_version(connection, migration_state.target_version) logger.info( "数据库 schema 版本写入完成。" f" 来源={migration_state.resolved_version.source.value}," f" 写入版本={migration_state.target_version}" ) def _build_noop_state( self, current_version: int, target_version: int, resolved_state: ResolvedSchemaVersion, ) -> DatabaseMigrationState: """构建无迁移动作的数据库状态对象。 Args: current_version: 当前数据库版本号。 target_version: 当前初始化流程期望达到的目标版本号。 resolved_state: 已解析的数据库版本状态。 Returns: DatabaseMigrationState: 不包含迁移步骤的状态对象。 """ return DatabaseMigrationState( resolved_version=resolved_state, target_version=target_version, plan=MigrationPlan(current_version=current_version, target_version=target_version, steps=[]), ) def create_database_migration_bootstrapper( engine: Engine, registry: Optional[MigrationRegistry] = None, resolver: Optional[SchemaVersionResolver] = None, version_store: Optional[SQLiteUserVersionStore] = None, latest_schema_version: int = LATEST_SCHEMA_VERSION, ) -> DatabaseMigrationBootstrapper: """创建数据库迁移启动桥接器。 Args: engine: 目标数据库引擎。 registry: 迁移步骤注册表;未提供时使用默认注册表。 resolver: 数据库版本解析器;未提供时使用默认解析器。 version_store: 版本存储器;未提供时使用默认存储器。 latest_schema_version: 当前代码支持的最新 schema 版本号。 Returns: DatabaseMigrationBootstrapper: 配置完成的数据库迁移启动桥接器。 """ migration_registry = registry or build_default_migration_registry() migration_resolver = resolver or build_default_schema_version_resolver() migration_version_store = version_store or SQLiteUserVersionStore() migration_manager = DatabaseMigrationManager( engine=engine, registry=migration_registry, resolver=migration_resolver, version_store=migration_version_store, ) return DatabaseMigrationBootstrapper( manager=migration_manager, latest_schema_version=latest_schema_version, )