diff --git a/src-tauri/src/commands/connection.rs b/src-tauri/src/commands/connection.rs index 7706ed528..b6a041596 100644 --- a/src-tauri/src/commands/connection.rs +++ b/src-tauri/src/commands/connection.rs @@ -106,7 +106,9 @@ impl AppState { PoolKind::MongoDb(client) } DatabaseType::ClickHouse => { - let client = db::clickhouse_driver::ChClient::new(&url); + let username = if db_config.username.is_empty() { None } else { Some(db_config.username.clone()) }; + let password = if db_config.password.is_empty() { None } else { Some(db_config.password.clone()) }; + let client = db::clickhouse_driver::ChClient::new(&url, username, password); db::clickhouse_driver::test_connection(&client).await?; PoolKind::ClickHouse(client) } @@ -311,7 +313,9 @@ pub async fn test_connection( Err(e) => Err(e.to_string()), }, DatabaseType::ClickHouse => { - let client = db::clickhouse_driver::ChClient::new(&url); + let username = if config.username.is_empty() { None } else { Some(config.username.clone()) }; + let password = if config.password.is_empty() { None } else { Some(config.password.clone()) }; + let client = db::clickhouse_driver::ChClient::new(&url, username, password); db::clickhouse_driver::test_connection(&client) .await .map(|_| "Connection successful".to_string()) @@ -381,7 +385,9 @@ pub async fn connect_db( PoolKind::MongoDb(client) } DatabaseType::ClickHouse => { - let client = db::clickhouse_driver::ChClient::new(&url); + let username = if config.username.is_empty() { None } else { Some(config.username.clone()) }; + let password = if config.password.is_empty() { None } else { Some(config.password.clone()) }; + let client = db::clickhouse_driver::ChClient::new(&url, username, password); db::clickhouse_driver::test_connection(&client).await?; PoolKind::ClickHouse(client) } diff --git a/src-tauri/src/commands/sql_file.rs b/src-tauri/src/commands/sql_file.rs index c3df7934a..252c761c7 100644 --- a/src-tauri/src/commands/sql_file.rs +++ b/src-tauri/src/commands/sql_file.rs @@ -67,6 +67,7 @@ pub struct SqlFileProgress { pub error: Option, } +#[cfg(test)] #[derive(Debug, Clone, PartialEq, Eq)] struct SqlFileSummary { status: SqlFileStatus, diff --git a/src-tauri/src/db/clickhouse_driver.rs b/src-tauri/src/db/clickhouse_driver.rs index b88855ed2..e47d58fce 100644 --- a/src-tauri/src/db/clickhouse_driver.rs +++ b/src-tauri/src/db/clickhouse_driver.rs @@ -7,13 +7,17 @@ use super::{ColumnInfo, DatabaseInfo, QueryResult, TableInfo}; pub struct ChClient { http: HttpClient, base_url: String, + username: Option, + password: Option, } impl ChClient { - pub fn new(url: &str) -> Self { + pub fn new(url: &str, username: Option, password: Option) -> Self { Self { http: HttpClient::new(), base_url: url.trim_end_matches('/').to_string(), + username, + password, } } } @@ -23,6 +27,8 @@ impl Clone for ChClient { Self { http: self.http.clone(), base_url: self.base_url.clone(), + username: self.username.clone(), + password: self.password.clone(), } } } @@ -43,13 +49,21 @@ struct ChColumn { _type: String, } +fn build_request(client: &ChClient, req: reqwest::RequestBuilder) -> reqwest::RequestBuilder { + match (&client.username, &client.password) { + (Some(u), Some(p)) if !u.is_empty() => req.basic_auth(u, Some(p)), + (Some(u), None) if !u.is_empty() => req.basic_auth(u, None::<&str>), + _ => req, + } +} + async fn ch_query(client: &ChClient, sql: &str, database: Option<&str>) -> Result { let mut url = format!("{}/?default_format=JSONCompact", client.base_url); if let Some(db) = database { url.push_str(&format!("&database={}", db)); } - let resp = client.http.post(&url) - .body(sql.to_string()) + let req = build_request(client, client.http.post(&url).body(sql.to_string())); + let resp = req .send() .await .map_err(|e| format!("ClickHouse request failed: {e}"))?; @@ -62,7 +76,8 @@ async fn ch_query(client: &ChClient, sql: &str, database: Option<&str>) -> Resul pub async fn test_connection(client: &ChClient) -> Result<(), String> { let url = format!("{}/ping", client.base_url); - client.http.get(&url).send().await + let req = build_request(client, client.http.get(&url)); + req.send().await .map_err(|e| format!("ClickHouse connection failed: {e}"))?; Ok(()) } @@ -139,8 +154,8 @@ pub async fn execute_query(client: &ChClient, database: &str, sql: &str) -> Resu }) } else { let url = format!("{}/?default_format=JSONCompact&database={}", client.base_url, database); - let resp = client.http.post(&url) - .body(sql.to_string()) + let req = build_request(client, client.http.post(&url).body(sql.to_string())); + let resp = req .send() .await .map_err(|e| format!("ClickHouse request failed: {e}"))?; diff --git a/src-tauri/src/models/connection.rs b/src-tauri/src/models/connection.rs index e59a8ca0e..dca3169a9 100644 --- a/src-tauri/src/models/connection.rs +++ b/src-tauri/src/models/connection.rs @@ -104,7 +104,7 @@ impl ConnectionConfig { }; format!("postgres://{host}:{port}{db_part}{suffix}") } - DatabaseType::ClickHouse => format!("http://{host}:{port}{db_part}"), + DatabaseType::ClickHouse => format!("http://{host}:{port}"), DatabaseType::SqlServer => format!( "server=tcp:{host},{port};database={}", self.database.as_deref().unwrap_or("master") @@ -161,7 +161,7 @@ impl ConnectionConfig { username, password ) } - DatabaseType::ClickHouse => format!("http://{host}:{port}{db_part}"), + DatabaseType::ClickHouse => format!("http://{host}:{port}"), DatabaseType::SqlServer => format!( "server=tcp:{host},{port};user={};password={};database={}", self.username,