From 18d59f82c9beaad621e178f7d89acfa660a744ae Mon Sep 17 00:00:00 2001 From: Cam Proudlock Date: Sun, 19 Jul 2026 00:36:54 -0400 Subject: [PATCH] ADR-014 schema-lean investigation + idempotent create_plugin_tables --- shopdb/plugins/alembic_template.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) 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)))