From d4286ab90afd7df14da82b666e7039bde9a06ffe Mon Sep 17 00:00:00 2001 From: Jason Date: Mon, 29 Dec 2025 17:21:06 +0800 Subject: [PATCH] fix: migrate proxy_config singleton to per-app on startup for v2 databases Add startup migration for legacy proxy_config tables that still have singleton structure (no app_type column) even with user_version=2. This fixes the issue where v3.9.0-2 databases with v2 schema but legacy proxy_config structure would fail with "no such column: app_type" error. - Call migrate_proxy_config_to_per_app in create_tables_on_conn - Add regression test to verify the fix --- src-tauri/src/database/schema.rs | 8 ++++++ src-tauri/src/database/tests.rs | 47 ++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/src-tauri/src/database/schema.rs b/src-tauri/src/database/schema.rs index 6e6167f6e..52187ae47 100644 --- a/src-tauri/src/database/schema.rs +++ b/src-tauri/src/database/schema.rs @@ -265,6 +265,14 @@ impl Database { [], ); + // 兼容:若旧版 proxy_config 仍为单例结构(无 app_type),则在启动时直接转换为三行结构 + // 说明:user_version=2 时不会再触发 v1->v2 迁移,但新代码查询依赖 app_type 列。 + if Self::table_exists(conn, "proxy_config")? + && !Self::has_column(conn, "proxy_config", "app_type")? + { + Self::migrate_proxy_config_to_per_app(conn)?; + } + // 确保 in_failover_queue 列存在(对于已存在的 v2 数据库) Self::add_column_if_missing( conn, diff --git a/src-tauri/src/database/tests.rs b/src-tauri/src/database/tests.rs index 74172fe58..de20a8b86 100644 --- a/src-tauri/src/database/tests.rs +++ b/src-tauri/src/database/tests.rs @@ -201,6 +201,53 @@ fn migration_aligns_column_defaults_and_types() { ); } +#[test] +fn create_tables_repairs_legacy_proxy_config_singleton_to_per_app() { + let conn = Connection::open_in_memory().expect("open memory db"); + + // 模拟测试版 v2:user_version=2,但 proxy_config 仍是单例结构(无 app_type) + Database::set_user_version(&conn, 2).expect("set user_version"); + conn.execute_batch( + r#" + CREATE TABLE proxy_config ( + id INTEGER PRIMARY KEY, + enabled INTEGER NOT NULL DEFAULT 0, + listen_address TEXT NOT NULL DEFAULT '127.0.0.1', + listen_port INTEGER NOT NULL DEFAULT 5000, + max_retries INTEGER NOT NULL DEFAULT 3, + request_timeout INTEGER NOT NULL DEFAULT 300, + enable_logging INTEGER NOT NULL DEFAULT 1, + target_app TEXT NOT NULL DEFAULT 'claude', + created_at TEXT NOT NULL DEFAULT (datetime('now')), + updated_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + INSERT INTO proxy_config (id, enabled) VALUES (1, 1); + "#, + ) + .expect("seed legacy proxy_config"); + + Database::create_tables_on_conn(&conn).expect("create tables should repair proxy_config"); + + assert!( + Database::has_column(&conn, "proxy_config", "app_type").expect("check app_type"), + "proxy_config should be migrated to per-app structure" + ); + + let count: i32 = conn + .query_row("SELECT COUNT(*) FROM proxy_config", [], |r| r.get(0)) + .expect("count rows"); + assert_eq!(count, 3, "per-app proxy_config should have 3 rows"); + + // 新结构下应能按 app_type 查询 + let _: i32 = conn + .query_row( + "SELECT COUNT(*) FROM proxy_config WHERE app_type = 'claude'", + [], + |r| r.get(0), + ) + .expect("query by app_type"); +} + #[test] fn dry_run_does_not_write_to_disk() { // Create minimal valid config for migration