diff --git a/shopdb/plugins/alembic_template.py b/shopdb/plugins/alembic_template.py index 67c87021..076f72ec 100644 --- a/shopdb/plugins/alembic_template.py +++ b/shopdb/plugins/alembic_template.py @@ -85,21 +85,24 @@ def _get_plugin_metadata(plugin_name: str) -> MetaData: def create_plugin_tables(plugin_name: str): - """Emit CreateTable DDL for every table this plugin owns. Idempotent - via Alembic's batch_op.create_table behavior (raises if exists; the - baseline migration is meant to run against an empty schema). + """Create every table this plugin owns, sourced from the SQLAlchemy models + (not duplicated DDL). IDEMPOTENT: a table that already exists is skipped, so + this is safe on an existing database that has the table from the pre-cutover + core baseline as well as on a fresh install (ADR-014 Phase 2). - Called from each plugin's 0001_baseline.py upgrade() so the table - definitions stay sourced from the SQLAlchemy models rather than being - duplicated in handwritten Alembic ops. + Called from each plugin's 0001 baseline.py upgrade(). """ from alembic import op + from sqlalchemy import inspect from sqlalchemy.schema import CreateTable md = _get_plugin_metadata(plugin_name) bind = op.get_bind() + existing = set(inspect(bind).get_table_names()) # Sort by FK dependency so parent tables are created first. for table in md.sorted_tables: + if table.name in existing: + continue op.execute(str(CreateTable(table).compile(dialect=bind.dialect)))