diff --git a/app/services/schema_upgrade.py b/app/services/schema_upgrade.py new file mode 100644 index 0000000..2bba207 --- /dev/null +++ b/app/services/schema_upgrade.py @@ -0,0 +1,58 @@ +""" +Lightweight, idempotent schema top-ups. + +`db.create_all()` creates missing tables but never alters existing ones, so a +new column on an existing table needs help. This adds only what's missing and +is safe to run on every boot, on both SQLite and Postgres. + +It is not a replacement for real migrations — it handles additive column +changes, which is all LIM has needed so far. + +Created by: Jess Rogerson (yelling commands at Claude.AI) +""" + +from sqlalchemy import inspect, text + +from app import db + +# table -> column -> DDL fragment used in ALTER TABLE ... ADD COLUMN +ADDITIVE_COLUMNS = { + 'sets': { + # Existing rows keep the behaviour they had before visibility existed: + # everyone could see every set, so they become 'public' rather than + # silently vanishing from other users' libraries. New sets use the + # site default instead. + 'visibility': "VARCHAR(10) NOT NULL DEFAULT 'public'", + }, +} + + +def ensure_schema(app): + """Add any missing columns. Returns a list of changes made.""" + applied = [] + with app.app_context(): + try: + inspector = inspect(db.engine) + existing_tables = set(inspector.get_table_names()) + except Exception as exc: # database not reachable yet + app.logger.warning('Schema check skipped: %s', exc) + return applied + + for table, columns in ADDITIVE_COLUMNS.items(): + if table not in existing_tables: + continue # create_all() will build it with everything present + present = {c['name'] for c in inspector.get_columns(table)} + for column, ddl in columns.items(): + if column in present: + continue + statement = f'ALTER TABLE {table} ADD COLUMN {column} {ddl}' + try: + with db.engine.begin() as conn: + conn.execute(text(statement)) + applied.append(f'{table}.{column}') + app.logger.info('Schema upgrade: added %s.%s', table, column) + except Exception as exc: + app.logger.error('Schema upgrade failed for %s.%s: %s', + table, column, exc) + + return applied