feat: use native postgres path for GaussDB connections

This commit is contained in:
t8y2 committed 2026-05-25 19:25:35 +08:00
1 parent 792c816a17
commit 234099aebd
9 files changed
+196 -34

No files matched your search

@@ -192,12 +192,11 @@
{
"dbType": "gaussdb",
"label": "GaussDB",
"runtimeMode": "agent",
"mcpMode": "bridge",
"agentKey": "gaussdb",
"runtimeMode": "native",
"mcpMode": "direct",
"singleConnectionPool": false,
"metadataConnectionScoped": false,
"skipTcpProbe": true,
"skipTcpProbe": false,
"defaultPort": 8000
},
{
@@ -280,12 +279,11 @@
{
"dbType": "opengauss",
"label": "openGauss",
"runtimeMode": "agent",
"mcpMode": "bridge",
"agentKey": "opengauss",
"runtimeMode": "native",
"mcpMode": "direct",
"singleConnectionPool": false,
"metadataConnectionScoped": false,
"skipTcpProbe": true,
"skipTcpProbe": false,
"defaultPort": 5432
},
{
-2
View File
@@ -23,7 +23,6 @@ pub const AGENT_TYPES: &[(&str, &str)] = &[
("vertica", "Vertica"),
("firebird", "Firebird"),
("exasol", "Exasol"),
("opengauss", "openGauss"),
("oceanbase-oracle", "OceanBase Oracle Mode"),
("gbase", "GBase"),
("access", "Microsoft Access"),
@@ -40,7 +39,6 @@ pub const AGENT_TYPES: &[(&str, &str)] = &[
("bigquery", "Google BigQuery"),
("kylin", "Apache Kylin"),
("sundb", "SunDB"),
("gaussdb", "GaussDB"),
("yashandb", "崖山 YashanDB"),
("tdengine", "TDengine"),
("mongodb", "MongoDB (Legacy)"),
+167 -6
View File
@@ -190,7 +190,9 @@ impl AppState {
DatabaseType::Doris | DatabaseType::StarRocks => {
PoolKind::Mysql(db::mysql::connect_bare(&url).await?, MysqlMode::Bare)
}
DatabaseType::Postgres | DatabaseType::Redshift => PoolKind::Postgres(db::postgres::connect(&url).await?),
DatabaseType::Postgres | DatabaseType::Redshift | DatabaseType::Gaussdb | DatabaseType::OpenGauss => {
PoolKind::Postgres(db::postgres::connect(&url).await?)
}
DatabaseType::Sqlite => PoolKind::Sqlite(db::sqlite::connect_path(&expand_tilde(&db_config.host)).await?),
DatabaseType::Redis => {
let con = db::redis_driver::connect(&url).await?;
@@ -268,7 +270,6 @@ impl AppState {
| DatabaseType::Vertica
| DatabaseType::Firebird
| DatabaseType::Exasol
| DatabaseType::OpenGauss
| DatabaseType::OceanbaseOracle
| DatabaseType::Gbase
| DatabaseType::Oracle
@@ -284,8 +285,7 @@ impl AppState {
| DatabaseType::Kylin
| DatabaseType::Sundb
| DatabaseType::Tdengine
| DatabaseType::Access
| DatabaseType::Gaussdb => {
| DatabaseType::Access => {
let connect_params =
agent_connect_params(&db_config, &host, port, db_config.effective_database().unwrap_or(""));
let mut client =
@@ -586,6 +586,8 @@ fn default_dbx_dir() -> PathBuf {
}
pub fn connection_url_for_endpoint(config: &ConnectionConfig, host: &str, port: u16) -> String {
let normalized = native_postgres_url_config(config);
let config = normalized.as_ref().unwrap_or(config);
if host == config.host && port == config.port {
config.connection_url()
} else {
@@ -594,6 +596,8 @@ pub fn connection_url_for_endpoint(config: &ConnectionConfig, host: &str, port:
}
pub fn redacted_connection_url_for_endpoint(config: &ConnectionConfig, host: &str, port: u16) -> String {
let normalized = native_postgres_url_config(config);
let config = normalized.as_ref().unwrap_or(config);
if host == config.host && port == config.port {
config.redacted_connection_url()
} else {
@@ -601,6 +605,27 @@ pub fn redacted_connection_url_for_endpoint(config: &ConnectionConfig, host: &st
}
}
fn native_postgres_url_config(config: &ConnectionConfig) -> Option<ConnectionConfig> {
match config.db_type {
DatabaseType::Gaussdb | DatabaseType::OpenGauss => {
let mut normalized = config.clone();
if config.db_type == DatabaseType::Gaussdb {
let params = normalized.url_params.as_deref().unwrap_or("").trim().trim_start_matches('?');
if !params.to_lowercase().contains("sslmode=") {
normalized.url_params = Some(if params.is_empty() {
"sslmode=disable".to_string()
} else {
format!("sslmode=disable&{params}")
});
}
}
normalized.db_type = DatabaseType::Postgres;
Some(normalized)
}
_ => None,
}
}
pub fn agent_connect_params(config: &ConnectionConfig, host: &str, port: u16, database: &str) -> serde_json::Value {
let agent_database = if config.db_type == DatabaseType::MongoDb {
mongo_agent_database(config, database)
@@ -808,9 +833,10 @@ async fn detect_ob_oracle_mode(config: &ConnectionConfig, pool: &db::mysql::MySq
#[cfg(test)]
mod tests {
use super::{
agent_connect_params, database_connection_config, metadata_connection_config, uses_tcp_probe, AppState,
PoolKind,
agent_connect_params, connection_url_for_endpoint, database_connection_config, metadata_connection_config,
redacted_connection_url_for_endpoint, uses_tcp_probe, AppState, PoolKind,
};
use crate::db;
use crate::models::connection::{ConnectionConfig, DatabaseType, ProxyType};
use crate::schema;
use crate::storage::Storage;
@@ -1029,6 +1055,37 @@ mod tests {
(AppState::new(storage), dir)
}
fn live_postgres_like_config(
db_type: DatabaseType,
host: &str,
port: u16,
username: &str,
password: &str,
url_params: Option<&str>,
) -> ConnectionConfig {
let mut config = mysql_config(Some("postgres"));
config.db_type = db_type;
config.host = host.to_string();
config.port = port;
config.username = username.to_string();
config.password = password.to_string();
config.url_params = url_params.map(str::to_string);
config
}
async fn assert_live_postgres_like_query(config: ConnectionConfig) {
let url = connection_url_for_endpoint(&config, &config.host, config.port);
let pool = db::postgres::connect(&url).await.unwrap_or_else(|err| {
panic!("failed to connect to {:?} at {}:{}: {}", config.db_type, config.host, config.port, err)
});
let result =
db::postgres::execute_query(&pool, "SELECT current_database(), current_schema()").await.unwrap_or_else(
|err| panic!("failed to query {:?} at {}:{}: {}", config.db_type, config.host, config.port, err),
);
assert_eq!(result.rows.len(), 1);
pool.close();
}
#[test]
fn mysql_metadata_connection_ignores_saved_default_database() {
let config = mysql_config(Some("app"));
@@ -1058,6 +1115,64 @@ mod tests {
assert_eq!(scoped.database.as_deref(), Some("analytics"));
}
#[test]
fn gaussdb_endpoint_url_uses_postgres_scheme_for_native_driver() {
let mut config = mysql_config(Some("postgres"));
config.db_type = DatabaseType::Gaussdb;
config.username = "gaussdb".to_string();
config.password = "secret".to_string();
assert_eq!(
connection_url_for_endpoint(&config, &config.host, config.port),
"postgres://gaussdb:secret@127.0.0.1:3306/postgres?sslmode=disable"
);
assert_eq!(
redacted_connection_url_for_endpoint(&config, &config.host, config.port),
"postgres://127.0.0.1:3306/postgres?sslmode=disable"
);
}
#[test]
fn opengauss_endpoint_url_uses_postgres_scheme_for_native_driver() {
let mut config = mysql_config(Some("postgres"));
config.db_type = DatabaseType::OpenGauss;
config.username = "gaussdb".to_string();
config.password = "secret".to_string();
assert_eq!(
connection_url_for_endpoint(&config, &config.host, config.port),
"postgres://gaussdb:secret@127.0.0.1:3306/postgres"
);
}
#[test]
fn gaussdb_endpoint_url_keeps_explicit_sslmode() {
let mut config = mysql_config(Some("postgres"));
config.db_type = DatabaseType::Gaussdb;
config.username = "gaussdb".to_string();
config.password = "secret".to_string();
config.url_params = Some("sslmode=require&application_name=dbx".to_string());
assert_eq!(
connection_url_for_endpoint(&config, &config.host, config.port),
"postgres://gaussdb:secret@127.0.0.1:3306/postgres?sslmode=require&application_name=dbx"
);
}
#[test]
fn gaussdb_endpoint_url_prepends_default_sslmode_to_custom_params() {
let mut config = mysql_config(Some("postgres"));
config.db_type = DatabaseType::Gaussdb;
config.username = "gaussdb".to_string();
config.password = "secret".to_string();
config.url_params = Some("application_name=dbx".to_string());
assert_eq!(
connection_url_for_endpoint(&config, &config.host, config.port),
"postgres://gaussdb:secret@127.0.0.1:3306/postgres?sslmode=disable&application_name=dbx"
);
}
#[test]
fn mongodb_database_connection_keeps_saved_database_for_auth() {
let mut config = mysql_config(Some("admin"));
@@ -1193,4 +1308,50 @@ mod tests {
state.proxy_tunnels.stop_tunnel("proxied").await;
let _ = std::fs::remove_dir_all(dir);
}
#[tokio::test]
#[ignore = "requires a reachable GaussDB instance via environment variables"]
async fn live_gaussdb_native_connection_succeeds() {
let host = std::env::var("DBX_TEST_GAUSSDB_HOST").expect("DBX_TEST_GAUSSDB_HOST not set");
let port = std::env::var("DBX_TEST_GAUSSDB_PORT")
.expect("DBX_TEST_GAUSSDB_PORT not set")
.parse::<u16>()
.expect("DBX_TEST_GAUSSDB_PORT should be a u16");
let username = std::env::var("DBX_TEST_GAUSSDB_USER").expect("DBX_TEST_GAUSSDB_USER not set");
let password = std::env::var("DBX_TEST_GAUSSDB_PASSWORD").expect("DBX_TEST_GAUSSDB_PASSWORD not set");
let url_params = std::env::var("DBX_TEST_GAUSSDB_URL_PARAMS").ok();
assert_live_postgres_like_query(live_postgres_like_config(
DatabaseType::Gaussdb,
&host,
port,
&username,
&password,
url_params.as_deref(),
))
.await;
}
#[tokio::test]
#[ignore = "requires a reachable openGauss instance via environment variables"]
async fn live_opengauss_native_connection_succeeds() {
let host = std::env::var("DBX_TEST_OPENGAUSS_HOST").expect("DBX_TEST_OPENGAUSS_HOST not set");
let port = std::env::var("DBX_TEST_OPENGAUSS_PORT")
.expect("DBX_TEST_OPENGAUSS_PORT not set")
.parse::<u16>()
.expect("DBX_TEST_OPENGAUSS_PORT should be a u16");
let username = std::env::var("DBX_TEST_OPENGAUSS_USER").expect("DBX_TEST_OPENGAUSS_USER not set");
let password = std::env::var("DBX_TEST_OPENGAUSS_PASSWORD").expect("DBX_TEST_OPENGAUSS_PASSWORD not set");
let url_params = std::env::var("DBX_TEST_OPENGAUSS_URL_PARAMS").ok();
assert_live_postgres_like_query(live_postgres_like_config(
DatabaseType::OpenGauss,
&host,
port,
&username,
&password,
url_params.as_deref(),
))
.await;
}
}
@@ -14,7 +14,6 @@ pub fn agent_key(db_type: &DatabaseType, driver_profile: Option<&str>) -> Option
DatabaseType::Vertica => Some("vertica"),
DatabaseType::Firebird => Some("firebird"),
DatabaseType::Exasol => Some("exasol"),
DatabaseType::OpenGauss => Some("opengauss"),
DatabaseType::OceanbaseOracle => Some("oceanbase-oracle"),
DatabaseType::Gbase => Some("gbase"),
DatabaseType::Oracle => match driver_profile {
@@ -32,7 +31,6 @@ pub fn agent_key(db_type: &DatabaseType, driver_profile: Option<&str>) -> Option
DatabaseType::Bigquery => Some("bigquery"),
DatabaseType::Kylin => Some("kylin"),
DatabaseType::Sundb => Some("sundb"),
DatabaseType::Gaussdb => Some("gaussdb"),
DatabaseType::MongoDb => Some("mongodb"),
DatabaseType::Tdengine => Some("tdengine"),
DatabaseType::Access => Some("access"),
+1
View File
@@ -122,6 +122,7 @@ fn pg_value_to_json(row: &Row, idx: usize, type_name: &str) -> serde_json::Value
pub async fn connect(url: &str) -> Result<Pool, String> {
validate_postgres_ssl_paths(url)?;
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
let tz = iana_time_zone::get_timezone().unwrap_or_else(|_| "UTC".to_string());
@@ -49,7 +49,6 @@ fn driver_manifest() -> DriverManifest {
fn maps_agent_database_types_to_driver_keys() {
assert_eq!(agent_key(&DatabaseType::Trino, None), Some("trino"));
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::Yashandb, None), Some("yashandb"));
assert_eq!(agent_key(&DatabaseType::Databricks, None), Some("databricks"));
@@ -58,7 +57,6 @@ fn maps_agent_database_types_to_driver_keys() {
assert_eq!(agent_key(&DatabaseType::Vertica, None), Some("vertica"));
assert_eq!(agent_key(&DatabaseType::Firebird, None), Some("firebird"));
assert_eq!(agent_key(&DatabaseType::Exasol, None), Some("exasol"));
assert_eq!(agent_key(&DatabaseType::OpenGauss, None), Some("opengauss"));
assert_eq!(agent_key(&DatabaseType::OceanbaseOracle, None), Some("oceanbase-oracle"));
assert_eq!(agent_key(&DatabaseType::Gbase, None), Some("gbase"));
assert_eq!(agent_key(&DatabaseType::Access, None), Some("access"));
@@ -80,12 +78,13 @@ fn classifies_agent_database_types() {
assert!(is_agent_type(&DatabaseType::Vertica));
assert!(is_agent_type(&DatabaseType::Firebird));
assert!(is_agent_type(&DatabaseType::Exasol));
assert!(is_agent_type(&DatabaseType::OpenGauss));
assert!(is_agent_type(&DatabaseType::OceanbaseOracle));
assert!(is_agent_type(&DatabaseType::Gbase));
assert!(is_agent_type(&DatabaseType::Access));
assert!(!is_agent_type(&DatabaseType::Mysql));
assert!(!is_agent_type(&DatabaseType::Jdbc));
assert!(!is_agent_type(&DatabaseType::Gaussdb));
assert!(!is_agent_type(&DatabaseType::OpenGauss));
}
#[test]
@@ -128,6 +127,8 @@ fn skips_tcp_probe_for_local_file_plugin_and_agent_types() {
assert!(skips_tcp_probe(&DatabaseType::Gbase));
assert!(!skips_tcp_probe(&DatabaseType::Postgres));
assert!(!skips_tcp_probe(&DatabaseType::Mysql));
assert!(!skips_tcp_probe(&DatabaseType::Gaussdb));
assert!(!skips_tcp_probe(&DatabaseType::OpenGauss));
}
#[test]