From 1bcfd02de105bc70710c60df9cf774075a14c3e4 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Sun, 17 May 2026 09:57:04 +0800 Subject: [PATCH] feat: add Microsoft Access database support Add Access as a new database type with agent-based connectivity, UCanAccess JDBC URL parsing, backtick identifier quoting, file-path connection dialog, and PNG icons for Access/GoldenDB/Vastbase. --- crates/dbx-core/src/connection.rs | 2 + crates/dbx-core/src/database_capabilities.rs | 2 + crates/dbx-core/src/models/connection.rs | 3 ++ .../dbx-core/tests/database_capabilities.rs | 4 ++ public/icons/database/access.png | Bin 0 -> 34094 bytes public/icons/database/goldendb.png | Bin 0 -> 837 bytes public/icons/database/vastbase.png | Bin 0 -> 2051 bytes src-tauri/src/commands/agents.rs | 4 +- .../connection/ConnectionDialog.vue | 28 +++++++++--- src/components/icons/DatabaseIcon.vue | 19 ++++---- src/lib/connectionPresentation.ts | 5 ++- src/lib/connectionUrl.ts | 25 +++++++++++ src/lib/dataGridSql.ts | 3 +- src/lib/databaseCapabilitySets.ts | 6 ++- src/lib/databaseTableDataCapabilities.ts | 9 ++++ src/lib/tableSelectSql.ts | 2 +- src/stores/connectionStore.ts | 1 + src/types/database.ts | 1 + tests/agentDriverInstallHint.test.ts | 4 ++ tests/connectionPresentation.test.ts | 11 +++++ tests/connectionUrl.test.ts | 12 +++++ tests/connectionUrlPlaceholder.test.ts | 1 + tests/dataGridSql.test.ts | 41 ++++++++++++++++++ tests/databaseCapabilities.test.ts | 7 +++ tests/tableEditing.test.ts | 5 ++- tests/tableSelectSql.test.ts | 12 +++++ 26 files changed, 185 insertions(+), 22 deletions(-) create mode 100644 public/icons/database/access.png create mode 100644 public/icons/database/goldendb.png create mode 100644 public/icons/database/vastbase.png diff --git a/crates/dbx-core/src/connection.rs b/crates/dbx-core/src/connection.rs index 11f2f9e60..b47c4272f 100644 --- a/crates/dbx-core/src/connection.rs +++ b/crates/dbx-core/src/connection.rs @@ -239,6 +239,7 @@ impl AppState { | DatabaseType::Kylin | DatabaseType::Sundb | DatabaseType::Tdengine + | DatabaseType::Access | DatabaseType::Gaussdb => { let mut client = self.agent_manager.spawn(&db_config.db_type, db_config.driver_profile.as_deref()).await?; @@ -437,6 +438,7 @@ pub fn agent_connect_params(config: &ConnectionConfig, host: &str, port: u16, da "username": config.username, "password": config.password, "url_params": config.url_params.as_deref().unwrap_or(""), + "connection_string": config.connection_string.as_deref().unwrap_or(""), }) } diff --git a/crates/dbx-core/src/database_capabilities.rs b/crates/dbx-core/src/database_capabilities.rs index 49a9c8ea7..bdca40517 100644 --- a/crates/dbx-core/src/database_capabilities.rs +++ b/crates/dbx-core/src/database_capabilities.rs @@ -25,6 +25,7 @@ pub fn agent_key(db_type: &DatabaseType, driver_profile: Option<&str>) -> Option DatabaseType::Gaussdb => Some("gaussdb"), DatabaseType::MongoDb => Some("mongodb"), DatabaseType::Tdengine => Some("tdengine"), + DatabaseType::Access => Some("access"), _ => None, } } @@ -44,6 +45,7 @@ pub fn is_single_connection_pool(db_type: &DatabaseType) -> bool { | DatabaseType::Highgo | DatabaseType::Vastbase | DatabaseType::Goldendb + | DatabaseType::Access | DatabaseType::Jdbc ) } diff --git a/crates/dbx-core/src/models/connection.rs b/crates/dbx-core/src/models/connection.rs index e78482aeb..112e881d4 100644 --- a/crates/dbx-core/src/models/connection.rs +++ b/crates/dbx-core/src/models/connection.rs @@ -120,6 +120,7 @@ pub enum DatabaseType { Vastbase, Goldendb, Gaussdb, + Access, #[serde(rename = "h2")] H2, Snowflake, @@ -213,6 +214,7 @@ impl ConnectionConfig { DatabaseType::Sqlite | DatabaseType::DuckDb => { format!("{}?mode=rwc", self.host) } + DatabaseType::Access => self.host.clone(), DatabaseType::Redis => { let scheme = if self.ssl { "rediss" } else { "redis" }; format!("{scheme}://{host}:{port}/") @@ -284,6 +286,7 @@ impl ConnectionConfig { DatabaseType::Sqlite | DatabaseType::DuckDb => { format!("{}?mode=rwc", self.host) } + DatabaseType::Access => self.host.clone(), DatabaseType::Redis => { let scheme = if self.ssl { "rediss" } else { "redis" }; if self.username.is_empty() && self.password.is_empty() { diff --git a/crates/dbx-core/tests/database_capabilities.rs b/crates/dbx-core/tests/database_capabilities.rs index 1d1ffbb96..4337f3b12 100644 --- a/crates/dbx-core/tests/database_capabilities.rs +++ b/crates/dbx-core/tests/database_capabilities.rs @@ -9,6 +9,7 @@ fn maps_agent_database_types_to_driver_keys() { assert_eq!(agent_key(&DatabaseType::Hive, None), Some("hive")); assert_eq!(agent_key(&DatabaseType::Gaussdb, None), Some("gaussdb")); assert_eq!(agent_key(&DatabaseType::Tdengine, None), Some("tdengine")); + assert_eq!(agent_key(&DatabaseType::Access, None), Some("access")); assert_eq!(agent_key(&DatabaseType::Oracle, None), Some("oracle")); assert_eq!(agent_key(&DatabaseType::Oracle, Some("oracle-10g")), Some("oracle-10g")); assert_eq!(agent_key(&DatabaseType::Postgres, None), None); @@ -20,6 +21,7 @@ fn classifies_agent_database_types() { assert!(is_agent_type(&DatabaseType::Trino)); assert!(is_agent_type(&DatabaseType::Hive)); assert!(is_agent_type(&DatabaseType::Tdengine)); + assert!(is_agent_type(&DatabaseType::Access)); assert!(!is_agent_type(&DatabaseType::Mysql)); assert!(!is_agent_type(&DatabaseType::Jdbc)); } @@ -30,6 +32,7 @@ fn identifies_single_connection_pool_types() { assert!(is_single_connection_pool(&DatabaseType::DuckDb)); assert!(is_single_connection_pool(&DatabaseType::Oracle)); assert!(is_single_connection_pool(&DatabaseType::Dameng)); + assert!(is_single_connection_pool(&DatabaseType::Access)); assert!(is_single_connection_pool(&DatabaseType::Jdbc)); assert!(!is_single_connection_pool(&DatabaseType::Trino)); assert!(!is_single_connection_pool(&DatabaseType::Postgres)); @@ -49,6 +52,7 @@ fn skips_tcp_probe_for_local_file_plugin_and_agent_types() { assert!(skips_tcp_probe(&DatabaseType::Sqlite)); assert!(skips_tcp_probe(&DatabaseType::DuckDb)); assert!(skips_tcp_probe(&DatabaseType::Jdbc)); + assert!(skips_tcp_probe(&DatabaseType::Access)); assert!(skips_tcp_probe(&DatabaseType::Trino)); assert!(skips_tcp_probe(&DatabaseType::Oracle)); assert!(skips_tcp_probe(&DatabaseType::Tdengine)); diff --git a/public/icons/database/access.png b/public/icons/database/access.png new file mode 100644 index 0000000000000000000000000000000000000000..44138a10e7d1665cf32803d6f3761f74c66fd55f GIT binary patch literal 34094 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 BHP-+D literal 0 HcmV?d00001 diff --git a/public/icons/database/goldendb.png b/public/icons/database/goldendb.png new file mode 100644 index 0000000000000000000000000000000000000000..b635a723b1c893503d5376d90bd1216214037549 GIT binary patch literal 837 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7e6l0AZa85pY67#JE_7#My5g&JNk zFq9fFFuY1&V6d9Oz#v{QXIG#NP$ECTC&cy3+lQx?W*?uQba7+-h`X8k1pIgF#Th*uOiTD z#w2fd7uFy7FOLH`oCO|{#S9GG!XV7ZFl&wkP>{XE)7O>#6{~=dD&J@61ED}6)e_f; zl9a@fRIB8oR3OD*WME{bYhbEtXcl5|+{(}xV#((h_j7TdW~kosD^ss+=1G3e?8YFe_?9p)>EEsrYZGMK_ZLmyu}|{x*{z!+ z8XO~jc1(U0I9EX5;Sm3miTp9M?irhxTQ_oi4$ht#9#D8KgZG!udS(uTd#M~7BiC=A zx5l^j@;;6U7Vqz@E1jZrsy`{tb8><_cLihBTPBx#teM+f z-~W+4x6fy{v8tp-f_uu+h=q=FwM-k-rd_)BN$s|~1M91R==c8~XeQm*>%YLU>Vou6 z=Y0>ao@JhFFpK9#IDgpT3-fgxu9xO)Ud*#|W&1Am30d`$8*BngGQI>PO50{^E;*6N z`7~GF>V)MsVUI_-P7^l1559Oxl%+zxbl25(*5h11vv_J{_u1(F`uC}>{O^B8`zz9O UN|rAb2gWRer>mdKI;Vst0BOx>nE(I) literal 0 HcmV?d00001 diff --git a/public/icons/database/vastbase.png b/public/icons/database/vastbase.png new file mode 100644 index 0000000000000000000000000000000000000000..1007b05fa6b2d95e713966165c913299b98c2f87 GIT binary patch literal 2051 zcmV+e2>kbnP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91AfN*P1ONa40RR91AOHXW0IY^$^8f$`ZAnByR9Fe+mu-lgR~5(4dFGi( zSe9j7CcE3P1j&aYvPh7S4{4fWKC}?qfU#=QYHN+!u3C3DO7`aRn;1-`B9jv2n__Ydxx;$2wAd=w`kk~+bow^c z4Vu_?$yqP-eHQC$lQUTd@9w&c+yj4Yn@op^jtI0|*XDNS_i(Ub8&|^(ex% zi;|O5kU%MayELt?Zd$UuZN`C(4gNe1PA;WC$!|sOH8GpChX%WwI?cY#0T_2;YzMR% zLsK@<-=|0Cf~>HUlD9ug!GwN|6p{mWq`1-!6<2F&Pe_5kyj3K%=VXPR_XXIhVae$v zc-R2lCoAb&TDt>@O;S)Zv6%_at(V&U&a#ne8OT)0gSl;^`#Z$Y#447@v|rpkM$uLJcx z79`W4qxz$(zjTxK_^M&4=^vq@WU@$gjOdZeC!M#vwP(P&IXMB%;5}pe98r0+l8nz) zh;|B^g**$J;q^4e)1?0`mkWn`3(W6=`rL-)B{kVP;usXr@~moMBLcXS{* z2(o*k9o&MaccD;-d+v&{)aa8iWDy69bF!MU90@P>9!S11SFyalplbh+ zV6&P^EISqTlkx#E?E;vDkIi8F!%tT&NEN(S2`tlD;F?K|gS#qcQiOhsXpc*SZirqR z#J9%7hi^ZAyyOyn8mW(x9kZX%pN0P0@^U*1Zi$Nn5A5|Jzg69CwCM&LV+J5(I5EYO zIO%qLVY=vu3pS|vos$Is<)%BgR7)H@3lTSQQ4B2EXpEO>GV@7FgkNHAOO$L1M{k++ z?`X(-%lQ9}EyyMMj?F?ihsqZK-8yvBr9culRKo`8tUFeKpD});qoMz+Nwx8t*0X1u zB&|_yq$yb!D8 z-#3zK;E1lUWp6p8-sZk9QaTZX^`1q6C|78G&S4kFHe;{!-KslhXP9Ygnol-><2ND7 z(CtA{*G2u??a_N`4#-(NY4mRePulpP`6NeSnIn$V}^IHGgWbtldIt3=WI9AJCP^!|_Ip=8hWZDYX~aiRw&IQ3+wPL$tmwcY%_p#$9KMZo!Y7Vl^A zjKm(%AC~vKqCHz2F1A>KO4@np}{zeQ!s5981XCFX@md6C457MOcPLhfdlv1Mu&y z?WOJRA2v&V$5Uc0i(R7svK}i%EHv$O>f18VNk~oK#~|CHWl}lSx_HzHl?xQ?Y4{ww h@+xx|I@xNS`ww5{5n<|_>&*ZF002ovPDHLkV1l8o?P34` literal 0 HcmV?d00001 diff --git a/src-tauri/src/commands/agents.rs b/src-tauri/src/commands/agents.rs index 2dfaef853..00cc5cd33 100644 --- a/src-tauri/src/commands/agents.rs +++ b/src-tauri/src/commands/agents.rs @@ -21,6 +21,7 @@ const AGENT_TYPES: &[(&str, &str)] = &[ ("highgo", "瀚高 HighGo"), ("vastbase", "Vastbase"), ("goldendb", "GoldenDB"), + ("access", "Microsoft Access"), ("oracle", "Oracle"), ("oracle-10g", "Oracle 10g"), ("h2", "H2"), @@ -104,13 +105,14 @@ mod tests { use dbx_core::agent_manager::AgentManager; #[test] - fn built_in_agent_list_includes_tdengine() { + fn built_in_agent_list_includes_access_and_tdengine() { let dir = std::env::temp_dir().join(format!("dbx-agent-list-test-{}", uuid::Uuid::new_v4())); let manager = AgentManager::new_with_base_dir(dir.clone()); let agents = build_agent_list(&manager, None); assert!(agents.iter().any(|agent| agent.db_type == "tdengine" && agent.label == "TDengine")); + assert!(agents.iter().any(|agent| agent.db_type == "access" && agent.label == "Microsoft Access")); let _ = std::fs::remove_dir_all(dir); } diff --git a/src/components/connection/ConnectionDialog.vue b/src/components/connection/ConnectionDialog.vue index 59f555b6d..2c30a2aa3 100644 --- a/src/components/connection/ConnectionDialog.vue +++ b/src/components/connection/ConnectionDialog.vue @@ -130,6 +130,7 @@ const driverProfiles: Record< redis: { type: "redis", port: 6379, user: "", label: "Redis", icon: "redis" }, sqlite: { type: "sqlite", port: 0, user: "", label: "SQLite", icon: "sqlite" }, duckdb: { type: "duckdb", port: 0, user: "", label: "DuckDB", icon: "duckdb" }, + access: { type: "access", port: 0, user: "", label: "Microsoft Access", icon: "access" }, mongodb: { type: "mongodb", port: 27017, user: "", label: "MongoDB", icon: "mongodb" }, clickhouse: { type: "clickhouse", @@ -248,7 +249,7 @@ function applyProfile(val: string, preserveConnectionFields = false) { form.value.port = profile.port; form.value.username = profile.user; form.value.url_params = profile.urlParams || ""; - if (profile.type === "sqlite" || profile.type === "duckdb") { + if (profile.type === "sqlite" || profile.type === "duckdb" || profile.type === "access") { form.value.host = ""; } if (profile.type === "jdbc") { @@ -358,6 +359,7 @@ const iconTypeMap: Record = { mysql: "mysql", postgres: "postgres", sqlite: "sqlite", + access: "access", redis: "redis", mongodb: "mongodb", duckdb: "duckdb", @@ -402,6 +404,7 @@ const dbOptions = [ { value: "mysql", label: "MySQL" }, { value: "postgres", label: "PostgreSQL" }, { value: "sqlite", label: "SQLite" }, + { value: "access", label: "Microsoft Access" }, { value: "redis", label: "Redis" }, { value: "mongodb", label: "MongoDB" }, { value: "duckdb", label: "DuckDB" }, @@ -467,8 +470,15 @@ const selectedDbIcon = computed(() => iconTypeMap[selectedType.value] || selecte const isJdbcConnection = computed(() => form.value.db_type === "jdbc"); const connectionUrlPlaceholder = computed(() => getUrlPlaceholder(form.value.db_type)); -const canUseSsh = computed(() => form.value.db_type !== "sqlite"); -const canUseProxy = computed(() => form.value.db_type !== "sqlite" && form.value.db_type !== "duckdb"); +const filePathPlaceholder = computed(() => { + if (form.value.db_type === "duckdb") return "/path/to/database.duckdb"; + if (form.value.db_type === "access") return "/path/to/database.accdb"; + return "/path/to/database.db"; +}); +const canUseSsh = computed(() => form.value.db_type !== "sqlite" && form.value.db_type !== "access"); +const canUseProxy = computed( + () => form.value.db_type !== "sqlite" && form.value.db_type !== "duckdb" && form.value.db_type !== "access", +); const shouldShowAgentDriverInstallHint = computed(() => showAgentDriverInstallHint(form.value.db_type, agentDrivers.value), ); @@ -677,7 +687,9 @@ async function browseDbFilePath() { const filters = form.value.db_type === "duckdb" ? [{ name: "DuckDB", extensions: ["duckdb", "db"] }] - : [{ name: "SQLite", extensions: ["db", "sqlite", "sqlite3"] }]; + : form.value.db_type === "access" + ? [{ name: "Microsoft Access", extensions: ["accdb", "mdb"] }] + : [{ name: "SQLite", extensions: ["db", "sqlite", "sqlite3"] }]; const selected = await open({ title: "Select Database File", multiple: false, @@ -1040,12 +1052,14 @@ function openExternalUrl(url: string) { - -