diff --git a/app.py b/app.py index 5642ba1..f7221aa 100644 --- a/app.py +++ b/app.py @@ -56,7 +56,7 @@ from kline_store import ensure_kline_tables from kline_stream import kline_hub, sse_format from kline_chart import generate_review_kline_chart, fetch_market_klines, MARKET_PERIODS from market import get_price as market_get_price, set_ths_refresh_token, get_quote_source_label -from db_conn import OperationalError, connect_db, database_label, is_benign_migration_error, is_db_contention_error +from db_conn import OperationalError, connect_db, database_label, is_benign_migration_error, is_db_contention_error, is_schema_migration_error, rollback_if_postgres from admin_settings import save_admin_credentials from db_backup import ( backup_dir, @@ -373,8 +373,9 @@ def init_db(): try: c.execute(sql) except Exception as exc: - if not is_benign_migration_error(exc): + if not is_schema_migration_error(exc): raise + rollback_if_postgres(conn) c.execute('''CREATE TABLE IF NOT EXISTS review_records (id INTEGER PRIMARY KEY AUTOINCREMENT, open_time TEXT, close_time TEXT, @@ -427,8 +428,9 @@ def init_db(): try: c.execute(sql) except Exception as exc: - if not is_benign_migration_error(exc): + if not is_schema_migration_error(exc): raise + rollback_if_postgres(conn) ensure_kline_tables(conn) init_strategy_tables(conn) from risk.account_risk_lib import ensure_account_risk_schema diff --git a/db_conn.py b/db_conn.py index b5ba17b..60a295a 100644 --- a/db_conn.py +++ b/db_conn.py @@ -92,6 +92,13 @@ def adapt_sql(sql: str) -> str: def is_benign_migration_error(exc: BaseException) -> bool: """ALTER TABLE 重复列等初始化迁移可忽略的错误。""" + if is_schema_migration_error(exc): + return True + return False + + +def is_schema_migration_error(exc: BaseException) -> bool: + """init_db 增量迁移:缺表/缺列/重复列均可忽略。""" msg = str(exc).lower() if any( x in msg @@ -99,18 +106,32 @@ def is_benign_migration_error(exc: BaseException) -> bool: "duplicate column", "already exists", "duplicate key", + "no such table", + "does not exist", + "undefined table", + "undefined column", ) ): return True - if isinstance(exc, sqlite3.OperationalError) and "duplicate column" in msg: + if isinstance(exc, sqlite3.OperationalError) and ( + "duplicate column" in msg or "no such table" in msg + ): return True if _PSYCOPG_OK and isinstance(exc, PgOperationalError): code = getattr(exc, "sqlstate", "") or "" - if code in ("42701", "42P07"): # duplicate_column, duplicate_table + if code in ("42701", "42P07", "42P01", "42703"): return True return False +def rollback_if_postgres(conn: "DbConnection") -> None: + if is_postgres(): + try: + conn.rollback() + except Exception: + pass + + class DbCursor: """统一 cursor:兼容 sqlite3 的 execute / fetchone / lastrowid。"""