149 lines
4.6 KiB
Python
149 lines
4.6 KiB
Python
"""
|
|
Lightweight, idempotent schema updates for SQLite.
|
|
|
|
Adds newly introduced columns to existing tables when running on an
|
|
already-initialized database. Safe to call multiple times.
|
|
"""
|
|
from typing import Dict
|
|
from sqlalchemy.engine import Engine
|
|
from sqlalchemy import text
|
|
|
|
|
|
def _existing_columns(conn, table: str) -> set[str]:
|
|
rows = conn.execute(text(f"PRAGMA table_info('{table}')")).fetchall()
|
|
return {row[1] for row in rows} # name is column 2
|
|
|
|
|
|
def ensure_schema_updates(engine: Engine) -> None:
|
|
"""Ensure missing columns are added for backward-compatible updates."""
|
|
# Map of table -> {column: SQL type}
|
|
updates: Dict[str, Dict[str, str]] = {
|
|
# Forms
|
|
"form_index": {
|
|
"keyword": "TEXT",
|
|
},
|
|
# Richer Life/Number tables (forms & pensions harmonized)
|
|
"life_tables": {
|
|
"le_aa": "FLOAT",
|
|
"na_aa": "FLOAT",
|
|
"le_am": "FLOAT",
|
|
"na_am": "FLOAT",
|
|
"le_af": "FLOAT",
|
|
"na_af": "FLOAT",
|
|
"le_wa": "FLOAT",
|
|
"na_wa": "FLOAT",
|
|
"le_wm": "FLOAT",
|
|
"na_wm": "FLOAT",
|
|
"le_wf": "FLOAT",
|
|
"na_wf": "FLOAT",
|
|
"le_ba": "FLOAT",
|
|
"na_ba": "FLOAT",
|
|
"le_bm": "FLOAT",
|
|
"na_bm": "FLOAT",
|
|
"le_bf": "FLOAT",
|
|
"na_bf": "FLOAT",
|
|
"le_ha": "FLOAT",
|
|
"na_ha": "FLOAT",
|
|
"le_hm": "FLOAT",
|
|
"na_hm": "FLOAT",
|
|
"le_hf": "FLOAT",
|
|
"na_hf": "FLOAT",
|
|
"table_year": "INTEGER",
|
|
"table_type": "VARCHAR(45)",
|
|
},
|
|
"number_tables": {
|
|
"month": "INTEGER",
|
|
"na_aa": "FLOAT",
|
|
"na_am": "FLOAT",
|
|
"na_af": "FLOAT",
|
|
"na_wa": "FLOAT",
|
|
"na_wm": "FLOAT",
|
|
"na_wf": "FLOAT",
|
|
"na_ba": "FLOAT",
|
|
"na_bm": "FLOAT",
|
|
"na_bf": "FLOAT",
|
|
"na_ha": "FLOAT",
|
|
"na_hm": "FLOAT",
|
|
"na_hf": "FLOAT",
|
|
"table_type": "VARCHAR(45)",
|
|
"description": "TEXT",
|
|
},
|
|
"form_list": {
|
|
"status": "VARCHAR(45)",
|
|
},
|
|
# Printers: add advanced legacy fields
|
|
"printers": {
|
|
"number": "INTEGER",
|
|
"page_break": "VARCHAR(50)",
|
|
"setup_st": "VARCHAR(200)",
|
|
"reset_st": "VARCHAR(200)",
|
|
"b_underline": "VARCHAR(100)",
|
|
"e_underline": "VARCHAR(100)",
|
|
"b_bold": "VARCHAR(100)",
|
|
"e_bold": "VARCHAR(100)",
|
|
"phone_book": "BOOLEAN",
|
|
"rolodex_info": "BOOLEAN",
|
|
"envelope": "BOOLEAN",
|
|
"file_cabinet": "BOOLEAN",
|
|
"accounts": "BOOLEAN",
|
|
"statements": "BOOLEAN",
|
|
"calendar": "BOOLEAN",
|
|
},
|
|
# Pensions
|
|
"pension_schedules": {
|
|
"vests_on": "DATE",
|
|
"vests_at": "FLOAT",
|
|
"version": "VARCHAR(10)",
|
|
},
|
|
"marriage_history": {
|
|
"married_from": "DATE",
|
|
"married_to": "DATE",
|
|
"married_years": "FLOAT",
|
|
"service_from": "DATE",
|
|
"service_to": "DATE",
|
|
"service_years": "FLOAT",
|
|
"marital_percent": "FLOAT",
|
|
"version": "VARCHAR(10)",
|
|
},
|
|
"death_benefits": {
|
|
"lump1": "FLOAT",
|
|
"lump2": "FLOAT",
|
|
"growth1": "FLOAT",
|
|
"growth2": "FLOAT",
|
|
"disc1": "FLOAT",
|
|
"disc2": "FLOAT",
|
|
"version": "VARCHAR(10)",
|
|
},
|
|
"separation_agreements": {
|
|
"version": "VARCHAR(10)",
|
|
},
|
|
# QDROs: add explicit created_at and workflow fields if missing
|
|
"qdros": {
|
|
"created_at": "DATETIME",
|
|
"approval_status": "VARCHAR(45)",
|
|
"approved_date": "DATE",
|
|
"filed_date": "DATE",
|
|
},
|
|
# Users: add approver flag
|
|
"users": {
|
|
"is_approver": "BOOLEAN",
|
|
},
|
|
}
|
|
|
|
with engine.begin() as conn:
|
|
for table, cols in updates.items():
|
|
try:
|
|
existing = _existing_columns(conn, table)
|
|
except Exception:
|
|
# Table may not exist yet
|
|
continue
|
|
for col_name, col_type in cols.items():
|
|
if col_name not in existing:
|
|
try:
|
|
conn.execute(text(f"ALTER TABLE {table} ADD COLUMN {col_name} {col_type}"))
|
|
except Exception:
|
|
# Ignore if not applicable (other engines) or race condition
|
|
pass
|
|
|
|
|