diff --git a/apps/desktop/src/components/connection/ConnectionDialog.vue b/apps/desktop/src/components/connection/ConnectionDialog.vue index 237882f33..a3e543c91 100644 --- a/apps/desktop/src/components/connection/ConnectionDialog.vue +++ b/apps/desktop/src/components/connection/ConnectionDialog.vue @@ -28,7 +28,7 @@ import { isLocalFileTypeDb } from "@/lib/connectionFile"; import { mongodbAuthFailureHint, mongoUrlParam, setMongoUrlParam } from "@/lib/mongoConnectionOptions"; import { copyToClipboard } from "@/lib/clipboard"; import { showAgentDriverInstallHint, type AgentDriverInstallState } from "@/lib/agentDriverInstallHint"; -import { ArrowLeft, ArrowDown, ArrowUp, CheckSquare, ChevronRight, Copy, ExternalLink, FilePlus2, FolderOpen, GripVertical, Grid3X3, KeyRound, Link2, List, ListFilter, Loader2, Pipette, Plus, Search, ShieldCheck, Square, Trash2 } from "@lucide/vue"; +import { ArrowLeft, ArrowDown, ArrowUp, CheckSquare, ChevronRight, CircleHelp, Copy, ExternalLink, FilePlus2, FolderOpen, GripVertical, Grid3X3, KeyRound, Link2, List, ListFilter, Loader2, Pipette, Plus, Search, ShieldCheck, Square, Trash2 } from "@lucide/vue"; import { buildDraftVisibleDatabasesConnectionId, connectionCanChooseVisibleDatabases, initialVisibleDatabaseSelection, visibleDatabaseSelectionIsStale } from "@/lib/connectionVisibleDatabases"; import { canSaveVisibleDatabaseSelection, filterDatabaseNamesForConnection, isSystemDatabaseName, normalizeVisibleDatabaseSelection } from "@/lib/visibleDatabases"; @@ -998,6 +998,13 @@ const mongoAuthMechanism = computed({ form.value.url_params = setMongoUrlParam(form.value.url_params, "authMechanism", value === "default" ? "" : value); }, }); +const mongoDriverMode = computed({ + get: () => (form.value.driver_profile === "mongodb-legacy" ? "legacy" : "auto"), + set: (value: string) => { + form.value.driver_profile = value === "legacy" ? "mongodb-legacy" : "mongodb"; + form.value.driver_label = value === "legacy" ? "MongoDB (Legacy)" : "MongoDB"; + }, +}); function goToConnectionStep(value = selectedType.value) { if (value !== selectedType.value) { @@ -1029,6 +1036,9 @@ async function testConnection() { const config = connectionConfigForSubmit(editingId.value || uuid()); const msg = await api.testConnection(config); if (runId !== testRunId) return; + if (config.db_type === "mongodb" && /legacy driver/i.test(msg)) { + mongoDriverMode.value = "legacy"; + } testResult.value = { ok: true, message: msg }; } catch (e: any) { if (runId !== testRunId) return; @@ -1101,6 +1111,10 @@ function connectionConfigForSubmit(id: string): ConnectionConfig { } else if (config.db_type === "mongodb") { config.connection_string = normalizeMongoConnectionString(config.connection_string?.trim() || ""); } + if (config.db_type === "mongodb" && config.driver_profile !== "mongodb-legacy") { + config.driver_profile = "mongodb"; + config.driver_label = "MongoDB"; + } if (config.db_type !== "oracle") { config.sysdba = undefined; config.oracle_connection_type = undefined; @@ -2493,6 +2507,21 @@ function openExternalUrl(url: string) { diff --git a/apps/desktop/src/components/icons/DatabaseIcon.vue b/apps/desktop/src/components/icons/DatabaseIcon.vue index d81ba5d6f..387224179 100644 --- a/apps/desktop/src/components/icons/DatabaseIcon.vue +++ b/apps/desktop/src/components/icons/DatabaseIcon.vue @@ -15,6 +15,7 @@ const assetIcons: Record = { turso: "turso.png", redis: "redis", mongodb: "mongodb", + mongodb_legacy: "mongodb", clickhouse: "clickhouse", duckdb: "duckdb", mariadb: "mariadb", diff --git a/apps/desktop/src/i18n/locales/en.ts b/apps/desktop/src/i18n/locales/en.ts index 2b89e5ae8..cd59ff833 100644 --- a/apps/desktop/src/i18n/locales/en.ts +++ b/apps/desktop/src/i18n/locales/en.ts @@ -159,6 +159,9 @@ authDatabasePlaceholder: "Optional, often admin", authMechanism: "Auth Mechanism", authMechanismDefault: "Default", + driverMode: "Driver", + mongoDriverAuto: "Auto", + mongoDriverLegacy: "Legacy", serviceName: "Service/SID", serviceNameOnly: "Service Name", version: "Version", @@ -327,7 +330,7 @@ jdbcPluginHint: "Install the DBX JDBC plugin first, then import the database vendor's JDBC driver JAR.", dmCompatHint: "Requires DM8 ODBC driver installed on your system.", dmDownload: "Download from Dameng", - mongoLegacyHint: "MongoDB below 4.2 requires the MongoDB (Legacy) driver. If authentication fails and the user was created in admin, set Auth DB to admin.", + mongoLegacyHint: "Use Legacy for older MongoDB servers, especially versions below 4.2. If authentication fails and the user was created in admin, set Auth DB to admin.", compatible: "Compatible", mainstream: "Popular", color: "Color", diff --git a/apps/desktop/src/i18n/locales/es.ts b/apps/desktop/src/i18n/locales/es.ts index 587db099c..58cf1f8b3 100644 --- a/apps/desktop/src/i18n/locales/es.ts +++ b/apps/desktop/src/i18n/locales/es.ts @@ -152,6 +152,9 @@ authDatabasePlaceholder: "Opcional, normalmente admin", authMechanism: "Mecanismo de autenticación", authMechanismDefault: "Predeterminado", + driverMode: "Controlador", + mongoDriverAuto: "Automático", + mongoDriverLegacy: "Legacy", serviceName: "Servicio/SID", serviceNameOnly: "Nombre de servicio", version: "Versión", @@ -306,7 +309,7 @@ jdbcPluginHint: "Instala primero el plugin JDBC de DBX y luego importa el JAR del driver JDBC del proveedor.", dmCompatHint: "Requiere el driver ODBC DM8 instalado en el sistema.", dmDownload: "Descargar desde Dameng", - mongoLegacyHint: "MongoDB inferior a 4.2 requiere el controlador MongoDB (Legacy) del Gestor de controladores; la conexión cambiará automáticamente.", + mongoLegacyHint: "Usa Legacy para servidores MongoDB antiguos, especialmente versiones anteriores a 4.2. Si falla la autenticación y el usuario se creó en admin, define Auth DB como admin.", compatible: "Compatible", mainstream: "Popular", color: "Color", diff --git a/apps/desktop/src/i18n/locales/it.ts b/apps/desktop/src/i18n/locales/it.ts index c4aabd999..3a66cf096 100644 --- a/apps/desktop/src/i18n/locales/it.ts +++ b/apps/desktop/src/i18n/locales/it.ts @@ -157,6 +157,9 @@ authDatabasePlaceholder: "Opzionale, spesso admin", authMechanism: "Meccanismo di Autenticazione", authMechanismDefault: "Predefinito", + driverMode: "Driver", + mongoDriverAuto: "Automatico", + mongoDriverLegacy: "Legacy", serviceName: "Servizio/SID", serviceNameOnly: "Nome Servizio", version: "Versione", @@ -311,7 +314,7 @@ jdbcPluginHint: "Installa prima il plugin DBX JDBC, quindi importa il file JAR del driver JDBC del fornitore del database.", dmCompatHint: "Richiede il driver ODBC DM8 installato sul sistema.", dmDownload: "Scarica da Dameng", - mongoLegacyHint: "MongoDB precedente a 4.2 richiede il driver MongoDB (Legacy). Se l'autenticazione fallisce e l'utente è stato creato in admin, imposta DB di Autenticazione su admin.", + mongoLegacyHint: "Usa Legacy per server MongoDB meno recenti, specialmente versioni precedenti alla 4.2. Se l'autenticazione fallisce e l'utente è stato creato in admin, imposta DB di Autenticazione su admin.", compatible: "Compatibile", mainstream: "Popolare", color: "Colore", diff --git a/apps/desktop/src/i18n/locales/pt-BR.ts b/apps/desktop/src/i18n/locales/pt-BR.ts index fd3f26039..8138975f1 100644 --- a/apps/desktop/src/i18n/locales/pt-BR.ts +++ b/apps/desktop/src/i18n/locales/pt-BR.ts @@ -157,6 +157,9 @@ authDatabasePlaceholder: "Opcional, geralmente admin", authMechanism: "Mecanismo de Autenticação", authMechanismDefault: "Padrão", + driverMode: "Driver", + mongoDriverAuto: "Automático", + mongoDriverLegacy: "Legacy", serviceName: "Service/SID", serviceNameOnly: "Service Name", version: "Versão", @@ -311,7 +314,7 @@ jdbcPluginHint: "Instale primeiro o plugin JDBC do DBX e depois importe o JAR do driver JDBC do fornecedor do banco de dados.", dmCompatHint: "Requer o driver ODBC DM8 instalado no seu sistema.", dmDownload: "Baixar da Dameng", - mongoLegacyHint: "MongoDB abaixo de 4.2 requer o driver MongoDB (Legacy). Se a autenticação falhar e o usuário foi criado em admin, defina o BD de autenticação como admin.", + mongoLegacyHint: "Use Legacy para servidores MongoDB antigos, especialmente versões anteriores à 4.2. Se a autenticação falhar e o usuário foi criado em admin, defina o BD de autenticação como admin.", compatible: "Compatível", mainstream: "Popular", color: "Cor", diff --git a/apps/desktop/src/i18n/locales/zh-CN.ts b/apps/desktop/src/i18n/locales/zh-CN.ts index 9b71282f1..e058e83ae 100644 --- a/apps/desktop/src/i18n/locales/zh-CN.ts +++ b/apps/desktop/src/i18n/locales/zh-CN.ts @@ -160,6 +160,9 @@ authDatabasePlaceholder: "可选,通常为 admin", authMechanism: "认证机制", authMechanismDefault: "默认", + driverMode: "驱动", + mongoDriverAuto: "自动", + mongoDriverLegacy: "旧版兼容", serviceName: "服务名/SID", serviceNameOnly: "服务名", version: "版本", @@ -328,7 +331,7 @@ jdbcPluginHint: "先安装 DBX JDBC 插件,再导入数据库厂商提供的 JDBC 驱动 JAR。", dmCompatHint: "需要在系统上安装达梦 DM8 ODBC 驱动程序。", dmDownload: "前往达梦官网下载", - mongoLegacyHint: "MongoDB 4.2 以下版本需安装 MongoDB (Legacy) 驱动。若账号创建在 admin 且认证失败,请将认证库设为 admin。", + mongoLegacyHint: "旧版 MongoDB(尤其是 4.2 以下版本)请选择旧版兼容。若账号创建在 admin 且认证失败,请将认证库设为 admin。", compatible: "兼容", mainstream: "主流", color: "颜色", diff --git a/apps/desktop/src/i18n/locales/zh-TW.ts b/apps/desktop/src/i18n/locales/zh-TW.ts index 6e27b78cc..485778295 100644 --- a/apps/desktop/src/i18n/locales/zh-TW.ts +++ b/apps/desktop/src/i18n/locales/zh-TW.ts @@ -157,6 +157,9 @@ authDatabasePlaceholder: "可選,通常為 admin", authMechanism: "認證機制", authMechanismDefault: "預設", + driverMode: "驅動", + mongoDriverAuto: "自動", + mongoDriverLegacy: "舊版相容", serviceName: "服務/SID", serviceNameOnly: "服務名稱", version: "版本", @@ -311,7 +314,7 @@ jdbcPluginHint: "先安裝 DBX JDBC 外掛程式,再匯入資料庫廠商提供的 JDBC 驅動程式 JAR。", dmCompatHint: "需要在系統上安裝達夢 DM8 ODBC 驅動程式。", dmDownload: "前往達夢官網下載", - mongoLegacyHint: "MongoDB 4.2 以下版本需安裝 MongoDB (Legacy) 驅動程式。若帳號建立在 admin 且認證失敗,請將認證庫設為 admin。", + mongoLegacyHint: "舊版 MongoDB(尤其是 4.2 以下版本)請選擇舊版相容。若帳號建立在 admin 且認證失敗,請將認證庫設為 admin。", compatible: "相容", mainstream: "熱門", color: "顏色", diff --git a/apps/desktop/src/lib/agentDriverInstallHint.ts b/apps/desktop/src/lib/agentDriverInstallHint.ts index 50df1b247..dbf78f9fc 100644 --- a/apps/desktop/src/lib/agentDriverInstallHint.ts +++ b/apps/desktop/src/lib/agentDriverInstallHint.ts @@ -8,6 +8,7 @@ export interface AgentDriverInstallState { function agentDriverInstallKey(dbType: DatabaseType | undefined, driverProfile?: string): string | undefined { if (dbType === "oracle") return "oracle"; + if (dbType === "mongodb") return "mongodb"; return driverProfile && driverProfile !== dbType ? driverProfile : dbType; } diff --git a/crates/dbx-core/src/agent_catalog.rs b/crates/dbx-core/src/agent_catalog.rs index 507c22561..2b1cfd1ce 100644 --- a/crates/dbx-core/src/agent_catalog.rs +++ b/crates/dbx-core/src/agent_catalog.rs @@ -27,6 +27,13 @@ const GBASE_PROFILES: &[AgentDriverProfile] = &[ AgentDriverProfile { profile: "gbase8a", key: "gbase8a", label: "GBase 8a", store_visible: true }, ]; +const MONGODB_PROFILES: &[AgentDriverProfile] = &[AgentDriverProfile { + profile: "mongodb-legacy", + key: "mongodb", + label: "MongoDB (Legacy)", + store_visible: false, +}]; + const AGENT_CATALOG: &[AgentCatalogEntry] = &[ AgentCatalogEntry { db_type: DatabaseType::Dameng, @@ -246,7 +253,7 @@ const AGENT_CATALOG: &[AgentCatalogEntry] = &[ key: "mongodb", label: "MongoDB (Legacy)", store_visible: true, - profiles: &[], + profiles: MONGODB_PROFILES, }, AgentCatalogEntry { db_type: DatabaseType::Iris, diff --git a/crates/dbx-core/src/agent_connection.rs b/crates/dbx-core/src/agent_connection.rs index eb11af88c..7b88f0a94 100644 --- a/crates/dbx-core/src/agent_connection.rs +++ b/crates/dbx-core/src/agent_connection.rs @@ -190,6 +190,30 @@ pub fn mongo_legacy_error_with_auth_hint(err: &str) -> String { ) } +pub fn mongo_uses_legacy_driver(config: &ConnectionConfig) -> bool { + config.driver_profile.as_deref().is_some_and(|profile| { + profile.eq_ignore_ascii_case("mongodb-legacy") + || profile.eq_ignore_ascii_case("mongodb_legacy") + || profile.eq_ignore_ascii_case("legacy") + }) +} + +pub fn should_retry_mongo_with_legacy_driver(err: &str) -> bool { + let normalized = err.to_lowercase(); + if normalized.contains("wire version") { + return true; + } + + let looks_like_handshake_io_error = normalized.contains("unexpected end of file") + || normalized.contains("connection reset by peer") + || normalized.contains("broken pipe"); + looks_like_handshake_io_error + && (normalized.contains("server selection timeout") + || normalized.contains("no available servers") + || normalized.contains("topology:") + || normalized.contains("i/o error")) +} + pub fn oracle_error_with_driver_hint(config: &ConnectionConfig, err: &str) -> String { if config.db_type != DatabaseType::Oracle { return err.to_string(); diff --git a/crates/dbx-core/src/connection.rs b/crates/dbx-core/src/connection.rs index 9ff5d16b6..a0dd88bca 100644 --- a/crates/dbx-core/src/connection.rs +++ b/crates/dbx-core/src/connection.rs @@ -8,8 +8,9 @@ use mysql_async::Row as MysqlRow; use crate::agent_connection::{ agent_connect_params, h2_file_path_from_jdbc_url, is_h2_file_connection, mongo_legacy_error_with_auth_hint, - oracle_alternate_connect_config_labels, oracle_alternate_connect_configs, oracle_auth_fallback_profiles, - oracle_error_with_driver_hint, should_retry_oracle_with_10g_driver, + mongo_uses_legacy_driver, oracle_alternate_connect_config_labels, oracle_alternate_connect_configs, + oracle_auth_fallback_profiles, oracle_error_with_driver_hint, should_retry_mongo_with_legacy_driver, + should_retry_oracle_with_10g_driver, }; use crate::agent_manager::{JavaRuntimeMode, DEFAULT_JRE_KEY}; use crate::database_capabilities; @@ -437,35 +438,49 @@ impl AppState { return Err("DuckDB support is not compiled in this build. Rebuild with default features.".to_string()); } DatabaseType::MongoDb => { - let native_err = match db::mongo_driver::connect(&url, connect_timeout, idle_timeout).await { - Ok(client) => match db::mongo_driver::test_connection( - &client, - connect_timeout, - db_config.effective_database(), - ) - .await - { - Ok(()) => { - let mut conns = self.connections.write().await; - // Re-check: another task may have created the pool while we were connecting. - if conns.contains_key(&pool_key) { - return Ok(pool_key); - } - conns.insert(pool_key.clone(), PoolKind::MongoDb(client)); - return Ok(pool_key); - } - Err(e) => e, - }, - Err(e) => e, - }; - if native_err.contains("wire version") { - log::info!("Native MongoDB driver failed ({native_err}), falling back to agent driver"); + if mongo_uses_legacy_driver(&db_config) { + log::info!("Using configured MongoDB legacy driver for connection_id={connection_id}"); let connect_params = serde_json::json!({ "connection": agent_connect_params(&db_config, &host, port, db_config.effective_database().unwrap_or("")) }); - let mut client = self.agent_manager.spawn(&DatabaseType::MongoDb, None).await?; + let mut client = self.agent_manager.spawn(&DatabaseType::MongoDb, Some("mongodb-legacy")).await?; client.connect(connect_params).await.map_err(|err| mongo_legacy_error_with_auth_hint(&err))?; PoolKind::Agent(Arc::new(tokio::sync::Mutex::new(client))) } else { - return Err(native_err); + let native_err = match db::mongo_driver::connect(&url, connect_timeout, idle_timeout).await { + Ok(client) => match db::mongo_driver::test_connection( + &client, + connect_timeout, + db_config.effective_database(), + ) + .await + { + Ok(()) => { + let mut conns = self.connections.write().await; + // Re-check: another task may have created the pool while we were connecting. + if conns.contains_key(&pool_key) { + return Ok(pool_key); + } + conns.insert(pool_key.clone(), PoolKind::MongoDb(client)); + return Ok(pool_key); + } + Err(e) => e, + }, + Err(e) => e, + }; + if should_retry_mongo_with_legacy_driver(&native_err) { + log::info!("Native MongoDB driver failed ({native_err}), falling back to agent driver"); + let connect_params = serde_json::json!({ "connection": agent_connect_params(&db_config, &host, port, db_config.effective_database().unwrap_or("")) }); + let mut client = + self.agent_manager.spawn(&DatabaseType::MongoDb, Some("mongodb-legacy")).await?; + client.connect(connect_params).await.map_err(|err| { + format!( + "{native_err}\n\nFallback with MongoDB (Legacy) driver failed: {}", + mongo_legacy_error_with_auth_hint(&err) + ) + })?; + PoolKind::Agent(Arc::new(tokio::sync::Mutex::new(client))) + } else { + return Err(native_err); + } } } DatabaseType::ClickHouse => { @@ -1297,8 +1312,8 @@ mod tests { AppState, PoolKind, }; use crate::agent_connection::{ - agent_connect_params, mongo_legacy_error_with_auth_hint, oracle_alternate_connect_config, - should_retry_oracle_with_10g_driver, + agent_connect_params, mongo_legacy_error_with_auth_hint, mongo_uses_legacy_driver, + oracle_alternate_connect_config, should_retry_mongo_with_legacy_driver, should_retry_oracle_with_10g_driver, }; use crate::agent_manager::{AgentState, JavaRuntimeConfig, JavaRuntimeMode, DEFAULT_JRE_KEY}; use crate::db; @@ -1433,6 +1448,19 @@ mod tests { ); } + #[test] + fn mongo_legacy_retry_covers_old_server_handshake_eof() { + let err = r#"MongoDB connection failed: Kind: Server selection timeout: No available servers. Topology: { Type: Unknown, Servers: [ { Address: db.example.com:27017, Type: Unknown, Error: Kind: I/O error: unexpected end of file } ] }"#; + + assert!(mongo_uses_legacy_driver(&ConnectionConfig { + driver_profile: Some("mongodb-legacy".to_string()), + ..mysql_config(None) + })); + assert!(should_retry_mongo_with_legacy_driver(err)); + assert!(should_retry_mongo_with_legacy_driver("server reports wire version 5, but this driver requires 8")); + assert!(!should_retry_mongo_with_legacy_driver("Authentication failed.")); + } + #[test] fn agent_connect_params_build_oracle_service_connection_string() { let mut config = mysql_config(Some("ORCLPDB1")); diff --git a/src-tauri/src/commands/connection.rs b/src-tauri/src/commands/connection.rs index 1863952b8..196ca0eb9 100644 --- a/src-tauri/src/commands/connection.rs +++ b/src-tauri/src/commands/connection.rs @@ -2,8 +2,9 @@ use std::sync::Arc; use tauri::State; pub use dbx_core::agent_connection::{ - agent_connect_params, mongo_legacy_error_with_auth_hint, oracle_alternate_connect_config, - oracle_auth_fallback_profiles, oracle_error_with_driver_hint, should_retry_oracle_with_10g_driver, + agent_connect_params, mongo_legacy_error_with_auth_hint, mongo_uses_legacy_driver, oracle_alternate_connect_config, + oracle_auth_fallback_profiles, oracle_error_with_driver_hint, should_retry_mongo_with_legacy_driver, + should_retry_oracle_with_10g_driver, }; pub use dbx_core::connection::{ connect_bare_metadata_pool, connect_mysql_metadata_pool, connection_url_for_endpoint, metadata_connection_config, @@ -333,6 +334,17 @@ pub async fn test_connection(state: State<'_, Arc>, config: Connection #[cfg(not(feature = "duckdb-bundled"))] DatabaseType::DuckDb => Err("DuckDB support not compiled (enable duckdb-bundled feature)".to_string()), DatabaseType::MongoDb => { + if mongo_uses_legacy_driver(&config) { + let am = &state.agent_manager; + let mut client = am.spawn(&config.db_type, config.driver_profile.as_deref()).await?; + client + .connect(mongo_legacy_connect_params(&config, &host, port)) + .await + .map_err(|err| mongo_legacy_error_with_auth_hint(&err))?; + client.disconnect().await.ok(); + return Ok("Connection successful (via legacy driver)".to_string()); + } + let native_err = match db::mongo_driver::connect(&url, connect_timeout, idle_timeout).await { Ok(client) => { match db::mongo_driver::test_connection(&client, connect_timeout, config.effective_database()) @@ -344,13 +356,15 @@ pub async fn test_connection(state: State<'_, Arc>, config: Connection } Err(e) => e, }; - if native_err.contains("wire version") { + if should_retry_mongo_with_legacy_driver(&native_err) { let am = &state.agent_manager; - let mut client = am.spawn(&config.db_type, config.driver_profile.as_deref()).await?; - client - .connect(mongo_legacy_connect_params(&config, &host, port)) - .await - .map_err(|err| mongo_legacy_error_with_auth_hint(&err))?; + let mut client = am.spawn(&config.db_type, Some("mongodb-legacy")).await?; + client.connect(mongo_legacy_connect_params(&config, &host, port)).await.map_err(|err| { + format!( + "{native_err}\n\nFallback with MongoDB (Legacy) driver failed: {}", + mongo_legacy_error_with_auth_hint(&err) + ) + })?; client.disconnect().await.ok(); Ok("Connection successful (via legacy driver)".to_string()) } else { @@ -542,29 +556,46 @@ pub async fn connect_db(state: State<'_, Arc>, config: ConnectionConfi #[cfg(not(feature = "duckdb-bundled"))] DatabaseType::DuckDb => return Err("DuckDB support not compiled (enable duckdb-bundled feature)".to_string()), DatabaseType::MongoDb => { - let native_err = match db::mongo_driver::connect(&url, connect_timeout, idle_timeout).await { - Ok(client) => { - match db::mongo_driver::test_connection(&client, connect_timeout, db_config.effective_database()) - .await - { - Ok(()) => { - state.configs.write().await.insert(id.clone(), config); - state.connections.write().await.insert(id.clone(), PoolKind::MongoDb(client)); - return Ok(id); - } - Err(e) => e, - } - } - Err(e) => e, - }; - if native_err.contains("wire version") { - log::info!("Native MongoDB driver failed ({native_err}), falling back to agent driver"); - let mut client = - state.agent_manager.spawn(&db_config.db_type, db_config.driver_profile.as_deref()).await?; - client.connect(mongo_legacy_connect_params(&db_config, &host, port)).await?; + if mongo_uses_legacy_driver(&db_config) { + let mut client = state.agent_manager.spawn(&db_config.db_type, Some("mongodb-legacy")).await?; + client + .connect(mongo_legacy_connect_params(&db_config, &host, port)) + .await + .map_err(|err| mongo_legacy_error_with_auth_hint(&err))?; PoolKind::Agent(std::sync::Arc::new(tokio::sync::Mutex::new(client))) } else { - return Err(native_err); + let native_err = match db::mongo_driver::connect(&url, connect_timeout, idle_timeout).await { + Ok(client) => { + match db::mongo_driver::test_connection( + &client, + connect_timeout, + db_config.effective_database(), + ) + .await + { + Ok(()) => { + state.configs.write().await.insert(id.clone(), config); + state.connections.write().await.insert(id.clone(), PoolKind::MongoDb(client)); + return Ok(id); + } + Err(e) => e, + } + } + Err(e) => e, + }; + if should_retry_mongo_with_legacy_driver(&native_err) { + log::info!("Native MongoDB driver failed ({native_err}), falling back to agent driver"); + let mut client = state.agent_manager.spawn(&db_config.db_type, Some("mongodb-legacy")).await?; + client.connect(mongo_legacy_connect_params(&db_config, &host, port)).await.map_err(|err| { + format!( + "{native_err}\n\nFallback with MongoDB (Legacy) driver failed: {}", + mongo_legacy_error_with_auth_hint(&err) + ) + })?; + PoolKind::Agent(std::sync::Arc::new(tokio::sync::Mutex::new(client))) + } else { + return Err(native_err); + } } } DatabaseType::ClickHouse => {