diff --git a/README.md b/README.md index c628b36018..150f2c5b9a 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ ### 40+ Databases, One Tool -MySQL, PostgreSQL, SQLite, Redis, MongoDB, DuckDB, ClickHouse, SQL Server, Oracle, Elasticsearch, MariaDB, TiDB, OceanBase, openGauss, GaussDB, KingBase, Vastbase, GoldenDB, Doris, SelectDB, StarRocks, Redshift, DM, TDengine, CockroachDB, Access, HighGo, and more. Agent/JDBC-oriented profiles extend DBX to H2, Snowflake, Trino, Hive, DB2, Informix, Neo4j, Cassandra, BigQuery, Kylin, SunDB, and custom JDBC connections. All in a single ~15 MB app. No bundled Chromium. +MySQL, PostgreSQL, SQLite, Redis, MongoDB, DuckDB, ClickHouse, SQL Server, Oracle, Elasticsearch, MariaDB, TiDB, OceanBase, openGauss, GaussDB, KingBase, Vastbase, GoldenDB, Doris, SelectDB, StarRocks, Redshift, DM, TDengine, XuguDB, CockroachDB, Access, HighGo, and more. Agent/JDBC-oriented profiles extend DBX to H2, Snowflake, Trino, Hive, DB2, Informix, Neo4j, Cassandra, BigQuery, Kylin, SunDB, and custom JDBC connections. All in a single ~15 MB app. No bundled Chromium. ### Query Editor @@ -290,7 +290,7 @@ DBX is 15 MB with no runtime dependencies (no Java, no Python). It includes AI a
What databases are supported? -MySQL, PostgreSQL, SQLite, Redis, MongoDB, DuckDB, ClickHouse, SQL Server, Oracle, Elasticsearch, MariaDB, TiDB, OceanBase, openGauss, GaussDB, KingBase, Vastbase, GoldenDB, Doris, SelectDB, StarRocks, Redshift, DM, TDengine, CockroachDB, Access, HighGo, and more. JDBC-oriented profiles extend support to H2, Snowflake, Trino, Hive, DB2, Informix, Neo4j, Cassandra, BigQuery, Kylin, SunDB, and custom JDBC connections. +MySQL, PostgreSQL, SQLite, Redis, MongoDB, DuckDB, ClickHouse, SQL Server, Oracle, Elasticsearch, MariaDB, TiDB, OceanBase, openGauss, GaussDB, KingBase, Vastbase, GoldenDB, Doris, SelectDB, StarRocks, Redshift, DM, TDengine, XuguDB, CockroachDB, Access, HighGo, and more. JDBC-oriented profiles extend support to H2, Snowflake, Trino, Hive, DB2, Informix, Neo4j, Cassandra, BigQuery, Kylin, SunDB, and custom JDBC connections.
diff --git a/README.zh-CN.md b/README.zh-CN.md index 55ceb23319..ded6d0d6c4 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -82,7 +82,7 @@ ### 40+ 种数据库,一个工具搞定 -MySQL、PostgreSQL、SQLite、Redis、MongoDB、DuckDB、ClickHouse、SQL Server、Oracle、Elasticsearch、MariaDB、TiDB、OceanBase、openGauss、GaussDB、KingBase、Vastbase、GoldenDB、Doris、SelectDB、StarRocks、Redshift、DM、TDengine、CockroachDB、Access、HighGo 等数据库都能直接连接。Agent/JDBC 方向的配置还可扩展到 H2、Snowflake、Trino、Hive、DB2、Informix、Neo4j、Cassandra、BigQuery、Kylin、SunDB 和自定义 JDBC。全部装进约 15 MB 的应用里,不内嵌 Chromium。 +MySQL、PostgreSQL、SQLite、Redis、MongoDB、DuckDB、ClickHouse、SQL Server、Oracle、Elasticsearch、MariaDB、TiDB、OceanBase、openGauss、GaussDB、KingBase、Vastbase、GoldenDB、Doris、SelectDB、StarRocks、Redshift、DM、TDengine、虚谷 XuguDB、CockroachDB、Access、HighGo 等数据库都能直接连接。Agent/JDBC 方向的配置还可扩展到 H2、Snowflake、Trino、Hive、DB2、Informix、Neo4j、Cassandra、BigQuery、Kylin、SunDB 和自定义 JDBC。全部装进约 15 MB 的应用里,不内嵌 Chromium。 ### 查询编辑器 @@ -290,7 +290,7 @@ DBX 仅 15 MB,无需运行时依赖(无需 Java、无需 Python)。AI 和
支持哪些数据库? -MySQL、PostgreSQL、SQLite、Redis、MongoDB、DuckDB、ClickHouse、SQL Server、Oracle、Elasticsearch、MariaDB、TiDB、OceanBase、openGauss、GaussDB、KingBase、Vastbase、GoldenDB、Doris、SelectDB、StarRocks、Redshift、DM、TDengine、CockroachDB、Access、HighGo 等。JDBC 方向配置可扩展到 H2、Snowflake、Trino、Hive、DB2、Informix、Neo4j、Cassandra、BigQuery、Kylin、SunDB 及自定义 JDBC 连接。 +MySQL、PostgreSQL、SQLite、Redis、MongoDB、DuckDB、ClickHouse、SQL Server、Oracle、Elasticsearch、MariaDB、TiDB、OceanBase、openGauss、GaussDB、KingBase、Vastbase、GoldenDB、Doris、SelectDB、StarRocks、Redshift、DM、TDengine、虚谷 XuguDB、CockroachDB、Access、HighGo 等。JDBC 方向配置可扩展到 H2、Snowflake、Trino、Hive、DB2、Informix、Neo4j、Cassandra、BigQuery、Kylin、SunDB 及自定义 JDBC 连接。
diff --git a/apps/desktop/public/icons/database/xugu.png b/apps/desktop/public/icons/database/xugu.png new file mode 100644 index 0000000000..e564ac2294 Binary files /dev/null and b/apps/desktop/public/icons/database/xugu.png differ diff --git a/apps/desktop/src/components/connection/ConnectionDialog.vue b/apps/desktop/src/components/connection/ConnectionDialog.vue index 19f687c8c0..582030b767 100644 --- a/apps/desktop/src/components/connection/ConnectionDialog.vue +++ b/apps/desktop/src/components/connection/ConnectionDialog.vue @@ -257,6 +257,7 @@ const driverProfiles: Record< sundb: { type: "sundb", port: 22000, user: "root", label: "SunDB", icon: "sundb" }, jdbc: { type: "jdbc", port: 0, user: "", label: "JDBC", icon: "jdbc" }, tdengine: { type: "tdengine", port: 6041, user: "root", label: "TDengine", icon: "tdengine" }, + xugu: { type: "xugu", port: 5138, user: "", label: "虚谷 XuguDB", icon: "xugu" }, iris: { type: "iris", port: 1972, user: "_SYSTEM", label: "IRIS", icon: "iris" }, custom_mysql: { type: "mysql", @@ -486,6 +487,7 @@ const iconTypeMap: Record = { redshift: "redshift", cockroachdb: "cockroachdb", tdengine: "tdengine", + xugu: "xugu", dm: "dm", h2: "h2", snowflake: "snowflake", @@ -554,6 +556,7 @@ const dbOptions = [ { value: "bigquery", label: "BigQuery" }, { value: "kylin", label: "Kylin" }, { value: "sundb", label: "SunDB" }, + { value: "xugu", label: "虚谷 XuguDB" }, { value: "iris", label: "IRIS" }, { value: "jdbc", label: "JDBC" }, { value: "custom_mysql", label: "Custom (MySQL)" }, diff --git a/apps/desktop/src/components/icons/DatabaseIcon.vue b/apps/desktop/src/components/icons/DatabaseIcon.vue index c1892dd5d9..76d88fd913 100644 --- a/apps/desktop/src/components/icons/DatabaseIcon.vue +++ b/apps/desktop/src/components/icons/DatabaseIcon.vue @@ -62,6 +62,7 @@ const assetIcons: Record = { tdsql: "tdsql.webp", polardb: "polardb.webp", greatsql: "greatsql.webp", + xugu: "xugu.png", iris: "iris.png", }; diff --git a/apps/desktop/src/lib/connectionPresentation.ts b/apps/desktop/src/lib/connectionPresentation.ts index 733d470a9b..be4eb0a166 100644 --- a/apps/desktop/src/lib/connectionPresentation.ts +++ b/apps/desktop/src/lib/connectionPresentation.ts @@ -70,6 +70,9 @@ export function connectionUrlPlaceholder(dbType: DatabaseType): string { case "tdengine": return "tdengine://user:password@host:6041/database"; + case "xugu": + return "xugu://user:password@host:5138/database"; + case "bigquery": return "bigquery://https://www.googleapis.com/bigquery/v2:443/project-id"; diff --git a/apps/desktop/src/lib/connectionUrl.ts b/apps/desktop/src/lib/connectionUrl.ts index 2ad2e44e7c..bdfc140ff0 100644 --- a/apps/desktop/src/lib/connectionUrl.ts +++ b/apps/desktop/src/lib/connectionUrl.ts @@ -45,6 +45,7 @@ const SCHEME_PROFILES: Record = { opengauss: { type: "gaussdb", profile: "opengauss", label: "openGauss", defaultPort: 5432 }, tdengine: { type: "tdengine", profile: "tdengine", label: "TDengine", defaultPort: 6041 }, "taos-ws": { type: "tdengine", profile: "tdengine", label: "TDengine", defaultPort: 6041 }, + xugu: { type: "xugu", profile: "xugu", label: "XuguDB", defaultPort: 5138 }, iris: { type: "iris", profile: "iris", label: "IRIS", defaultPort: 1972 }, }; diff --git a/apps/desktop/src/lib/databaseCapabilitySets.ts b/apps/desktop/src/lib/databaseCapabilitySets.ts index e34488a651..cebefe8bd7 100644 --- a/apps/desktop/src/lib/databaseCapabilitySets.ts +++ b/apps/desktop/src/lib/databaseCapabilitySets.ts @@ -25,6 +25,7 @@ export const SCHEMA_AWARE_TYPES = new Set([ "trino", "db2", "tdengine", + "xugu", "iris", "duckdb", ]); @@ -98,6 +99,7 @@ export const DATABASE_SEARCH_SUPPORTED_TYPES = new Set([ "kylin", "sundb", "tdengine", + "xugu", "iris", ]); @@ -206,6 +208,7 @@ export const TREE_SCHEMA_TYPES = new Set([ "trino", "h2", "tdengine", + "xugu", "iris", "duckdb", ]); @@ -243,6 +246,7 @@ export const AGENT_DRIVER_TYPES = new Set([ "sundb", "gaussdb", "tdengine", + "xugu", "iris", ]); diff --git a/apps/desktop/src/types/database.ts b/apps/desktop/src/types/database.ts index 111bd49256..5d1f5a2139 100644 --- a/apps/desktop/src/types/database.ts +++ b/apps/desktop/src/types/database.ts @@ -41,6 +41,7 @@ export type DatabaseType = | "kylin" | "sundb" | "tdengine" + | "xugu" | "iris" | "jdbc"; diff --git a/crates/dbx-core/assets/database-drivers.manifest.json b/crates/dbx-core/assets/database-drivers.manifest.json index 138b1a005c..ea8da538f6 100644 --- a/crates/dbx-core/assets/database-drivers.manifest.json +++ b/crates/dbx-core/assets/database-drivers.manifest.json @@ -449,6 +449,17 @@ "skipTcpProbe": true, "defaultPort": 6041 }, + { + "dbType": "xugu", + "label": "虚谷 XuguDB", + "runtimeMode": "agent", + "mcpMode": "bridge", + "agentKey": "xugu", + "singleConnectionPool": false, + "metadataConnectionScoped": false, + "skipTcpProbe": true, + "defaultPort": 5138 + }, { "dbType": "jdbc", "label": "JDBC", diff --git a/crates/dbx-core/src/agent_catalog.rs b/crates/dbx-core/src/agent_catalog.rs index 56881aac8e..17b589dba5 100644 --- a/crates/dbx-core/src/agent_catalog.rs +++ b/crates/dbx-core/src/agent_catalog.rs @@ -205,6 +205,13 @@ const AGENT_CATALOG: &[AgentCatalogEntry] = &[ store_visible: true, profiles: &[], }, + AgentCatalogEntry { + db_type: DatabaseType::Xugu, + key: "xugu", + label: "虚谷 XuguDB", + store_visible: true, + profiles: &[], + }, AgentCatalogEntry { db_type: DatabaseType::MongoDb, key: "mongodb", diff --git a/crates/dbx-core/src/connection.rs b/crates/dbx-core/src/connection.rs index 5f7437e130..d23c8e9874 100644 --- a/crates/dbx-core/src/connection.rs +++ b/crates/dbx-core/src/connection.rs @@ -300,6 +300,7 @@ impl AppState { | DatabaseType::Kylin | DatabaseType::Sundb | DatabaseType::Tdengine + | DatabaseType::Xugu | DatabaseType::Iris | DatabaseType::Access => { let connect_params = diff --git a/crates/dbx-core/src/models/connection.rs b/crates/dbx-core/src/models/connection.rs index 1afe119524..60c954a21a 100644 --- a/crates/dbx-core/src/models/connection.rs +++ b/crates/dbx-core/src/models/connection.rs @@ -190,6 +190,7 @@ pub enum DatabaseType { Kylin, Sundb, Tdengine, + Xugu, #[serde(rename = "iris")] Iris, Jdbc, @@ -377,6 +378,7 @@ impl ConnectionConfig { DatabaseType::Kylin => format!("kylin://{host}:{port}{db_part}"), DatabaseType::Sundb => format!("sundb://{host}:{port}{db_part}"), DatabaseType::Tdengine => format!("tdengine://{host}:{port}{db_part}"), + DatabaseType::Xugu => format!("xugu://{host}:{port}{db_part}"), DatabaseType::Iris => format!("iris://{host}:{port}{db_part}"), DatabaseType::Jdbc => "jdbc:".to_string(), } @@ -538,6 +540,9 @@ impl ConnectionConfig { DatabaseType::Tdengine => { format!("tdengine://{}:{}@{host}:{port}{db_part}", username, password) } + DatabaseType::Xugu => { + format!("xugu://{}:{}@{host}:{port}{db_part}", username, password) + } DatabaseType::Iris => { format!("iris://{}:{}@{host}:{port}{db_part}", username, password) } diff --git a/docs/content/docs/databases.cn.mdx b/docs/content/docs/databases.cn.mdx index ec289be9e2..eb5f60d321 100644 --- a/docs/content/docs/databases.cn.mdx +++ b/docs/content/docs/databases.cn.mdx @@ -18,7 +18,7 @@ DBX 主应用会尽量使用 Rust 原生驱动。部分数据库通过 MySQL 或 | MySQL 兼容类型 | MariaDB、TiDB、OceanBase、Doris、SelectDB、StarRocks、GoldenDB、自定义 MySQL | 复用 MySQL 风格连接能力 | | PostgreSQL 兼容类型 | openGauss、KingBase、HighGo、Vastbase、CockroachDB、自定义 PostgreSQL | 复用 PostgreSQL 风格连接能力 | | 文件型数据库 | SQLite、DuckDB、Microsoft Access | 选择本地数据库文件,不填写主机和端口 | -| Agent/JDBC 扩展类型 | H2、Snowflake、Trino、Hive、DB2、Informix、Neo4j、Cassandra、BigQuery、Kylin、SunDB、TDengine、JDBC | 功能覆盖取决于对应驱动路径 | +| Agent/JDBC 扩展类型 | H2、Snowflake、Trino、Hive、DB2、Informix、Neo4j、Cassandra、BigQuery、Kylin、SunDB、TDengine、虚谷 XuguDB、JDBC | 功能覆盖取决于对应驱动路径 | ## 默认端口 @@ -52,6 +52,7 @@ DBX 主应用会尽量使用 Rust 原生驱动。部分数据库通过 MySQL 或 | Kylin | 7070 | | SunDB | 22000 | | TDengine | 6041 | +| 虚谷 XuguDB | 5138 | SQLite、DuckDB、Access 和 JDBC 在不需要网络端口时,会在连接模型中保存为端口 `0`。 @@ -63,9 +64,9 @@ DBX 只会在具备足够元数据和 SQL 生成能力的数据库上启用高 | 功能 | 支持类型 | |---|---| -| Schema 感知树 | PostgreSQL、SQL Server、Oracle、Redshift、DM、GaussDB、KingBase、HighGo、Vastbase、JDBC、H2、Snowflake、Trino、DB2、TDengine | +| Schema 感知树 | PostgreSQL、SQL Server、Oracle、Redshift、DM、GaussDB、KingBase、HighGo、Vastbase、JDBC、H2、Snowflake、Trino、DB2、TDengine、虚谷 XuguDB | | ER 图 | MySQL、PostgreSQL、SQLite、SQL Server、Oracle、Redshift、DM、GaussDB、KingBase、HighGo、Vastbase、GoldenDB、Access、H2、DB2 | -| 数据库搜索 | MySQL、PostgreSQL、SQLite、SQL Server、Oracle、Redshift、DuckDB、ClickHouse、DM、GaussDB、KingBase、HighGo、Vastbase、GoldenDB、Access、H2、Snowflake、Trino、Hive、DB2、Informix、Neo4j、Cassandra、BigQuery、Kylin、SunDB、TDengine | +| 数据库搜索 | MySQL、PostgreSQL、SQLite、SQL Server、Oracle、Redshift、DuckDB、ClickHouse、DM、GaussDB、KingBase、HighGo、Vastbase、GoldenDB、Access、H2、Snowflake、Trino、Hive、DB2、Informix、Neo4j、Cassandra、BigQuery、Kylin、SunDB、TDengine、虚谷 XuguDB | | 表数据导入 | MySQL、PostgreSQL、SQLite、DuckDB、ClickHouse、SQL Server、Oracle、Doris、StarRocks、Redshift、DM、GaussDB、KingBase、HighGo、Vastbase、GoldenDB、Access | | 表结构编辑器 | MySQL、PostgreSQL、SQLite、SQL Server | | 创建数据库 | MySQL、PostgreSQL、SQL Server、ClickHouse、Oracle、DM、GaussDB、Doris、StarRocks、Redshift | @@ -81,7 +82,7 @@ DBX 只会在具备足够元数据和 SQL 生成能力的数据库上启用高 大多数网络数据库支持主机、端口、用户名、密码、默认数据库、URL 参数、SSL、连接颜色、SSH 隧道和代理设置。文件型数据库会改为选择本地文件。 -DBX 也可以解析常见连接 URL,例如 MySQL、PostgreSQL、Redis、MongoDB、ClickHouse、SQL Server、Oracle、Elasticsearch、DM、GaussDB、openGauss、TDengine 和 Access。 +DBX 也可以解析常见连接 URL,例如 MySQL、PostgreSQL、Redis、MongoDB、ClickHouse、SQL Server、Oracle、Elasticsearch、DM、GaussDB、openGauss、TDengine、虚谷 XuguDB 和 Access。 ### ClickHouse TLS diff --git a/docs/content/docs/databases.mdx b/docs/content/docs/databases.mdx index b396b0299b..ff76fa7bd0 100644 --- a/docs/content/docs/databases.mdx +++ b/docs/content/docs/databases.mdx @@ -18,7 +18,7 @@ The connection dialog exposes ready-to-use profiles with default ports and drive | MySQL-compatible profiles | MariaDB, TiDB, OceanBase, Doris, SelectDB, StarRocks, GoldenDB, custom MySQL | Reuse MySQL-style connection handling where the engine speaks a compatible protocol | | PostgreSQL-compatible profiles | openGauss, KingBase, HighGo, Vastbase, CockroachDB, custom PostgreSQL | Reuse PostgreSQL-style connection handling where the engine speaks a compatible protocol | | File-based engines | SQLite, DuckDB, Microsoft Access | Choose a local database file instead of host and port | -| Agent/JDBC-oriented engines | H2, Snowflake, Trino, Hive, DB2, Informix, Neo4j, Cassandra, BigQuery, Kylin, SunDB, TDengine, JDBC | Feature coverage depends on the driver path used by that engine | +| Agent/JDBC-oriented engines | H2, Snowflake, Trino, Hive, DB2, Informix, Neo4j, Cassandra, BigQuery, Kylin, SunDB, TDengine, XuguDB, JDBC | Feature coverage depends on the driver path used by that engine | ## Default Ports @@ -52,6 +52,7 @@ The connection dialog exposes ready-to-use profiles with default ports and drive | Kylin | 7070 | | SunDB | 22000 | | TDengine | 6041 | +| XuguDB | 5138 | SQLite, DuckDB, Access, and JDBC profiles use port `0` in the saved connection model when a network port is not part of the connection. @@ -63,9 +64,9 @@ DBX intentionally enables advanced workflows only where the app has enough metad | Feature | Supported Types | |---|---| -| Schema-aware tree | PostgreSQL, SQL Server, Oracle, Redshift, DM, GaussDB, KingBase, HighGo, Vastbase, JDBC, H2, Snowflake, Trino, DB2, TDengine | +| Schema-aware tree | PostgreSQL, SQL Server, Oracle, Redshift, DM, GaussDB, KingBase, HighGo, Vastbase, JDBC, H2, Snowflake, Trino, DB2, TDengine, XuguDB | | ER diagram | MySQL, PostgreSQL, SQLite, SQL Server, Oracle, Redshift, DM, GaussDB, KingBase, HighGo, Vastbase, GoldenDB, Access, H2, DB2 | -| Database search | MySQL, PostgreSQL, SQLite, SQL Server, Oracle, Redshift, DuckDB, ClickHouse, DM, GaussDB, KingBase, HighGo, Vastbase, GoldenDB, Access, H2, Snowflake, Trino, Hive, DB2, Informix, Neo4j, Cassandra, BigQuery, Kylin, SunDB, TDengine | +| Database search | MySQL, PostgreSQL, SQLite, SQL Server, Oracle, Redshift, DuckDB, ClickHouse, DM, GaussDB, KingBase, HighGo, Vastbase, GoldenDB, Access, H2, Snowflake, Trino, Hive, DB2, Informix, Neo4j, Cassandra, BigQuery, Kylin, SunDB, TDengine, XuguDB | | Table import | MySQL, PostgreSQL, SQLite, DuckDB, ClickHouse, SQL Server, Oracle, Doris, StarRocks, Redshift, DM, GaussDB, KingBase, HighGo, Vastbase, GoldenDB, Access | | Table structure editor | MySQL, PostgreSQL, SQLite, SQL Server | | Create database | MySQL, PostgreSQL, SQL Server, ClickHouse, Oracle, DM, GaussDB, Doris, StarRocks, Redshift | @@ -81,7 +82,7 @@ If a database can connect but does not appear in a feature row, that feature may Most network databases support host, port, username, password, default database, optional URL parameters, SSL, connection color, SSH tunnel, and proxy settings. File-based engines replace host and port with a file picker. -DBX can also parse common connection URLs for engines such as MySQL, PostgreSQL, Redis, MongoDB, ClickHouse, SQL Server, Oracle, Elasticsearch, DM, GaussDB, openGauss, TDengine, and Access. +DBX can also parse common connection URLs for engines such as MySQL, PostgreSQL, Redis, MongoDB, ClickHouse, SQL Server, Oracle, Elasticsearch, DM, GaussDB, openGauss, TDengine, XuguDB, and Access. ### ClickHouse TLS diff --git a/docs/content/docs/driver-management.cn.mdx b/docs/content/docs/driver-management.cn.mdx index effab3d052..e0b076bcbd 100644 --- a/docs/content/docs/driver-management.cn.mdx +++ b/docs/content/docs/driver-management.cn.mdx @@ -10,7 +10,7 @@ DBX 使用混合驱动架构:常用数据库使用内置原生驱动,需要 | 驱动类型 | 工作方式 | 适用场景 | |---|---|---| | 原生(内置) | Rust 驱动编译进 DBX | MySQL、PostgreSQL、SQLite、SQL Server、Oracle、Redis、MongoDB、DuckDB、ClickHouse 等 | -| JDBC Agent | DBX 管理的 Java 子进程 | 提供 JDBC 驱动的数据库:GaussDB、openGauss、DM、KingBase、HighGo、Vastbase、Trino、Hive、DB2、Informix、Neo4j、TDengine、YashanDB、GoldenDB、Kylin、SunDB 等 | +| JDBC Agent | DBX 管理的 Java 子进程 | 提供 JDBC 驱动的数据库:GaussDB、openGauss、DM、KingBase、HighGo、Vastbase、Trino、Hive、DB2、Informix、Neo4j、TDengine、虚谷 XuguDB、YashanDB、GoldenDB、Kylin、SunDB 等 | 原生驱动安装后立即可用。Agent 驱动需要一次性下载 JDBC 驱动和 JRE 设置,首次创建连接时 DBX 会自动处理。 diff --git a/docs/content/docs/driver-management.mdx b/docs/content/docs/driver-management.mdx index 4ed15fcc52..a1eda4708e 100644 --- a/docs/content/docs/driver-management.mdx +++ b/docs/content/docs/driver-management.mdx @@ -10,7 +10,7 @@ DBX uses a hybrid driver architecture: built-in native drivers for common databa | Driver Type | How It Works | Best For | |---|---|---| | Native (built-in) | Rust drivers compiled into DBX | MySQL, PostgreSQL, SQLite, SQL Server, Oracle, Redis, MongoDB, DuckDB, ClickHouse, and more | -| JDBC Agent | Java subprocess managed by DBX | Databases that provide JDBC drivers: GaussDB, openGauss, DM, KingBase, HighGo, Vastbase, Trino, Hive, DB2, Informix, Neo4j, TDengine, YashanDB, GoldenDB, Kylin, SunDB, and more | +| JDBC Agent | Java subprocess managed by DBX | Databases that provide JDBC drivers: GaussDB, openGauss, DM, KingBase, HighGo, Vastbase, Trino, Hive, DB2, Informix, Neo4j, TDengine, XuguDB, YashanDB, GoldenDB, Kylin, SunDB, and more | Native drivers work immediately after installation. Agent drivers require a one-time JDBC driver download and JRE setup, which DBX handles automatically when you first create a connection. diff --git a/packages/app-tests/connectionUrl.test.ts b/packages/app-tests/connectionUrl.test.ts index eba4db9937..8fd9f3892a 100644 --- a/packages/app-tests/connectionUrl.test.ts +++ b/packages/app-tests/connectionUrl.test.ts @@ -71,6 +71,20 @@ test("parses TDengine WebSocket JDBC URLs", () => { assert.equal(parsed.urlParams, "timezone=UTC"); }); +test("parses XuguDB JDBC URLs", () => { + const parsed = parseConnectionUrl("jdbc:xugu://alice:secret@xugu.example.com:5138/demo?charset=utf8"); + + assert.equal(parsed.dbType, "xugu"); + assert.equal(parsed.driverProfile, "xugu"); + assert.equal(parsed.driverLabel, "XuguDB"); + assert.equal(parsed.host, "xugu.example.com"); + assert.equal(parsed.port, 5138); + assert.equal(parsed.username, "alice"); + assert.equal(parsed.password, "secret"); + assert.equal(parsed.database, "demo"); + assert.equal(parsed.urlParams, "charset=utf8"); +}); + test("parses UCanAccess JDBC URLs as Access database files", () => { const parsed = parseConnectionUrl("jdbc:ucanaccess:///Users/me/data/Northwind.accdb;memory=false"); diff --git a/packages/app-tests/connectionUrlPlaceholder.test.ts b/packages/app-tests/connectionUrlPlaceholder.test.ts index 78c44bb658..970120ebea 100644 --- a/packages/app-tests/connectionUrlPlaceholder.test.ts +++ b/packages/app-tests/connectionUrlPlaceholder.test.ts @@ -20,6 +20,7 @@ const expected: Record = { elasticsearch: "http://user:password@host:port", dameng: "dm://user:password@host:port", tdengine: "tdengine://user:password@host:6041/database", + xugu: "xugu://user:password@host:5138/database", bigquery: "bigquery://https://www.googleapis.com/bigquery/v2:443/project-id", jdbc: "jdbc:mysql://host:3306/database", }; diff --git a/packages/app-tests/databaseCapabilities.test.ts b/packages/app-tests/databaseCapabilities.test.ts index e038d7873d..c466f2c366 100644 --- a/packages/app-tests/databaseCapabilities.test.ts +++ b/packages/app-tests/databaseCapabilities.test.ts @@ -35,6 +35,13 @@ test("treats TDengine databases as schema tree roots and agent driver databases" assert.equal(supportsDriverManagement("tdengine"), true); }); +test("treats XuguDB as a schema-aware agent driver database", () => { + assert.equal(TREE_SCHEMA_TYPES.has("xugu"), true); + assert.equal(SCHEMA_AWARE_TYPES.has("xugu"), true); + assert.equal(supportsDatabaseSearch("xugu"), true); + assert.equal(supportsDriverManagement("xugu"), true); +}); + test("treats Access as a local single-database agent driver", () => { assert.equal(SCHEMA_AWARE_TYPES.has("access"), false); assert.equal(supportsDriverManagement("access"), true); diff --git a/packages/mcp-server/src/index.ts b/packages/mcp-server/src/index.ts index 3685336f1f..7da4ec1a79 100644 --- a/packages/mcp-server/src/index.ts +++ b/packages/mcp-server/src/index.ts @@ -34,7 +34,7 @@ function withDatabase(config: ConnectionConfig, database?: string): ConnectionCo } export const DBX_CONNECTION_TYPE_DESCRIPTION = - "Database type: postgres, mysql, sqlite, redis, duckdb, clickhouse, sqlserver, mongodb, oracle, elasticsearch, doris, starrocks, redshift, dameng, kingbase, highgo, vastbase, goldendb, gaussdb, yashandb, databricks, saphana, teradata, vertica, firebird, exasol, opengauss, oceanbase-oracle, gbase, h2, snowflake, trino, hive, db2, informix, neo4j, cassandra, bigquery, kylin, sundb, tdengine, jdbc, access"; + "Database type: postgres, mysql, sqlite, redis, duckdb, clickhouse, sqlserver, mongodb, oracle, elasticsearch, doris, starrocks, redshift, dameng, kingbase, highgo, vastbase, goldendb, gaussdb, yashandb, databricks, saphana, teradata, vertica, firebird, exasol, opengauss, oceanbase-oracle, gbase, h2, snowflake, trino, hive, db2, informix, neo4j, cassandra, bigquery, kylin, sundb, tdengine, xugu, jdbc, access"; export function createDbxMcpServer(backend: Backend, options: { isWebMode?: boolean } = {}): McpServer { const isWebMode = options.isWebMode ?? !!process.env.DBX_WEB_URL; @@ -44,199 +44,208 @@ export function createDbxMcpServer(backend: Backend, options: { isWebMode?: bool }); server.tool("dbx_list_connections", "List all database connections configured in DBX", {}, async () => { - const connections = await backend.loadConnections(); - if (connections.length === 0) return text("No connections configured in DBX."); - const rows = connections.map((c) => [c.name, c.db_type, c.host, String(c.port), c.database || ""]); - return text(mdTable(["Name", "Type", "Host", "Port", "Database"], rows)); + const connections = await backend.loadConnections(); + if (connections.length === 0) return text("No connections configured in DBX."); + const rows = connections.map((c) => [c.name, c.db_type, c.host, String(c.port), c.database || ""]); + return text(mdTable(["Name", "Type", "Host", "Port", "Database"], rows)); }); server.tool( - "dbx_list_tables", - "List tables and views for a database connection", - { - connection_name: z.string().describe("Name of the DBX connection"), - database: z.string().optional().describe("Database name"), - schema: z.string().optional().describe("Schema name (default: public for PostgreSQL)"), - }, - async ({ connection_name, database, schema }) => { - const config = await backend.findConnection(connection_name); - if (!config) return text(`Connection "${connection_name}" not found`); - const tables = await backend.listTables(withDatabase(config, database), schema); - if (tables.length === 0) return text("No tables found."); - const rows = tables.map((t) => [t.name, t.type]); - return text(mdTable(["Table", "Type"], rows)); - }, - ); - - server.tool( - "dbx_describe_table", - "Get column definitions for a table", - { - connection_name: z.string().describe("Name of the DBX connection"), - table: z.string().describe("Table name"), - database: z.string().optional().describe("Database name"), - schema: z.string().optional().describe("Schema name (default: public for PostgreSQL)"), - }, - async ({ connection_name, table, database, schema }) => { - const config = await backend.findConnection(connection_name); - if (!config) return text(`Connection "${connection_name}" not found`); - const columns = await backend.describeTable(withDatabase(config, database), table, schema); - if (columns.length === 0) return text("No columns found."); - const rows = columns.map((c) => [ - c.is_primary_key ? `${c.name} (PK)` : c.name, - c.data_type, - c.is_nullable ? "YES" : "NO", - c.column_default ?? "", - c.comment ?? "", - ]); - return text(mdTable(["Column", "Type", "Nullable", "Default", "Comment"], rows)); - }, - ); - - server.tool( - "dbx_execute_query", - "Execute a SQL query on a database connection (max 100 rows returned)", - { - connection_name: z.string().describe("Name of the DBX connection"), - database: z.string().optional().describe("Database name"), - sql: z.string().describe("SQL query to execute"), - }, - async ({ connection_name, database, sql }) => { - const config = await backend.findConnection(connection_name); - if (!config) return text(`Connection "${connection_name}" not found`); - if (config.db_type !== "mongodb") { - const safety = evaluateSqlSafety(sql, sqlSafetyFromEnv()); - if (!safety.allowed) return text(`Query blocked: ${safety.reason}`); - } - // MongoDB shell commands don't fit the SQL safety evaluator; the backend - // (node-core executeQuery) applies command-aware read/write gating. - try { - const result = await backend.executeQuery(withDatabase(config, database), sql); - if (result.columns.length === 0) return text(`Query executed. ${result.row_count} row(s) affected.`); - const rows = result.rows.map((r) => result.columns.map((c) => formatCell(r[c]))); - return text(`${mdTable(result.columns, rows)}\n\n${result.row_count} row(s)`); - } catch (e: unknown) { - const msg = e instanceof Error ? e.message : String(e); - return text(`Query error: ${msg}`); - } - }, - ); - - server.tool( - "dbx_get_schema_context", - "Get compact table and column context for writing SQL", - { - connection_name: z.string().describe("Name of the DBX connection"), - database: z.string().optional().describe("Database name"), - schema: z.string().optional().describe("Schema name (default: public for PostgreSQL)"), - tables: z.array(z.string()).optional().describe("Specific table names to include"), - max_tables: z.number().int().min(1).max(20).default(8).describe("Maximum number of tables to include"), - }, - async ({ connection_name, database, schema, tables, max_tables }) => { - const config = await backend.findConnection(connection_name); - if (!config) return text(`Connection "${connection_name}" not found`); - const context = await buildSchemaContext(backend, withDatabase(config, database), { schema, tables, maxTables: max_tables }); - if (context.tables.length === 0) return text("No matching tables found."); - return text(formatSchemaContext(context)); - }, - ); - - server.tool( - "dbx_add_connection", - "Add a new database connection to DBX", - { - name: z.string().describe("Connection name"), - db_type: z - .string() - .describe(DBX_CONNECTION_TYPE_DESCRIPTION), - host: z.string().describe("Database host"), - port: z.number().optional().describe("Database port (TDengine defaults to 6041)"), - username: z.string().default("").describe("Username"), - password: z.string().default("").describe("Password"), - database: z.string().optional().describe("Default database name"), - ssl: z.boolean().default(false).describe("Enable SSL"), - }, - async ({ name, db_type, host, port, username, password, database, ssl }) => { - const existing = await backend.findConnection(name); - if (existing) return text(`Connection "${name}" already exists.`); - const FILE_BASED_TYPES = new Set(["sqlite", "duckdb", "access"]); - const resolvedPort = port ?? (db_type === "tdengine" ? 6041 : FILE_BASED_TYPES.has(db_type) ? 0 : undefined); - if (resolvedPort === undefined) return text("Port is required for this database type."); - const config = await backend.addConnection({ - name, - db_type, - host, - port: resolvedPort, - username, - password, - database, - ssl, - ssh_enabled: false, - } as Omit); - await notifyReload(); - return text(`Connection "${config.name}" added (id: ${config.id}).`); - }, - ); - - server.tool( - "dbx_remove_connection", - "Remove a database connection from DBX", - { - connection_name: z.string().describe("Name of the connection to remove"), - }, - async ({ connection_name }) => { - const removed = await backend.removeConnection(connection_name); - if (!removed) return text(`Connection "${connection_name}" not found.`); - await notifyReload(); - return text(`Connection "${connection_name}" removed.`); - }, - ); - -// Desktop-only tools: open table and execute-and-show require the Tauri bridge - if (!isWebMode) { - server.tool( - "dbx_open_table", - "Open a table in DBX desktop app UI. Requires DBX to be running.", + "dbx_list_tables", + "List tables and views for a database connection", { connection_name: z.string().describe("Name of the DBX connection"), - table: z.string().describe("Table name to open"), database: z.string().optional().describe("Database name"), - schema: z.string().optional().describe("Schema name"), + schema: z.string().optional().describe("Schema name (default: public for PostgreSQL)"), + }, + async ({ connection_name, database, schema }) => { + const config = await backend.findConnection(connection_name); + if (!config) return text(`Connection "${connection_name}" not found`); + const tables = await backend.listTables(withDatabase(config, database), schema); + if (tables.length === 0) return text("No tables found."); + const rows = tables.map((t) => [t.name, t.type]); + return text(mdTable(["Table", "Type"], rows)); + }, + ); + + server.tool( + "dbx_describe_table", + "Get column definitions for a table", + { + connection_name: z.string().describe("Name of the DBX connection"), + table: z.string().describe("Table name"), + database: z.string().optional().describe("Database name"), + schema: z.string().optional().describe("Schema name (default: public for PostgreSQL)"), }, async ({ connection_name, table, database, schema }) => { - return bridgeRequest("/open-table", { connection_name, table, database, schema }, `Opened ${table} in DBX`); + const config = await backend.findConnection(connection_name); + if (!config) return text(`Connection "${connection_name}" not found`); + const columns = await backend.describeTable(withDatabase(config, database), table, schema); + if (columns.length === 0) return text("No columns found."); + const rows = columns.map((c) => [ + c.is_primary_key ? `${c.name} (PK)` : c.name, + c.data_type, + c.is_nullable ? "YES" : "NO", + c.column_default ?? "", + c.comment ?? "", + ]); + return text(mdTable(["Column", "Type", "Nullable", "Default", "Comment"], rows)); }, + ); + + server.tool( + "dbx_execute_query", + "Execute a SQL query on a database connection (max 100 rows returned)", + { + connection_name: z.string().describe("Name of the DBX connection"), + database: z.string().optional().describe("Database name"), + sql: z.string().describe("SQL query to execute"), + }, + async ({ connection_name, database, sql }) => { + const config = await backend.findConnection(connection_name); + if (!config) return text(`Connection "${connection_name}" not found`); + if (config.db_type !== "mongodb") { + const safety = evaluateSqlSafety(sql, sqlSafetyFromEnv()); + if (!safety.allowed) return text(`Query blocked: ${safety.reason}`); + } + // MongoDB shell commands don't fit the SQL safety evaluator; the backend + // (node-core executeQuery) applies command-aware read/write gating. + try { + const result = await backend.executeQuery(withDatabase(config, database), sql); + if (result.columns.length === 0) return text(`Query executed. ${result.row_count} row(s) affected.`); + const rows = result.rows.map((r) => result.columns.map((c) => formatCell(r[c]))); + return text(`${mdTable(result.columns, rows)}\n\n${result.row_count} row(s)`); + } catch (e: unknown) { + const msg = e instanceof Error ? e.message : String(e); + return text(`Query error: ${msg}`); + } + }, + ); + + server.tool( + "dbx_get_schema_context", + "Get compact table and column context for writing SQL", + { + connection_name: z.string().describe("Name of the DBX connection"), + database: z.string().optional().describe("Database name"), + schema: z.string().optional().describe("Schema name (default: public for PostgreSQL)"), + tables: z.array(z.string()).optional().describe("Specific table names to include"), + max_tables: z.number().int().min(1).max(20).default(8).describe("Maximum number of tables to include"), + }, + async ({ connection_name, database, schema, tables, max_tables }) => { + const config = await backend.findConnection(connection_name); + if (!config) return text(`Connection "${connection_name}" not found`); + const context = await buildSchemaContext(backend, withDatabase(config, database), { + schema, + tables, + maxTables: max_tables, + }); + if (context.tables.length === 0) return text("No matching tables found."); + return text(formatSchemaContext(context)); + }, + ); + + server.tool( + "dbx_add_connection", + "Add a new database connection to DBX", + { + name: z.string().describe("Connection name"), + db_type: z.string().describe(DBX_CONNECTION_TYPE_DESCRIPTION), + host: z.string().describe("Database host"), + port: z.number().optional().describe("Database port (TDengine defaults to 6041, XuguDB defaults to 5138)"), + username: z.string().default("").describe("Username"), + password: z.string().default("").describe("Password"), + database: z.string().optional().describe("Default database name"), + ssl: z.boolean().default(false).describe("Enable SSL"), + }, + async ({ name, db_type, host, port, username, password, database, ssl }) => { + const existing = await backend.findConnection(name); + if (existing) return text(`Connection "${name}" already exists.`); + const FILE_BASED_TYPES = new Set(["sqlite", "duckdb", "access"]); + const DEFAULT_PORTS: Record = { tdengine: 6041, xugu: 5138 }; + const resolvedPort = port ?? DEFAULT_PORTS[db_type] ?? (FILE_BASED_TYPES.has(db_type) ? 0 : undefined); + if (resolvedPort === undefined) return text("Port is required for this database type."); + const config = await backend.addConnection({ + name, + db_type, + host, + port: resolvedPort, + username, + password, + database, + ssl, + ssh_enabled: false, + } as Omit); + await notifyReload(); + return text(`Connection "${config.name}" added (id: ${config.id}).`); + }, + ); + + server.tool( + "dbx_remove_connection", + "Remove a database connection from DBX", + { + connection_name: z.string().describe("Name of the connection to remove"), + }, + async ({ connection_name }) => { + const removed = await backend.removeConnection(connection_name); + if (!removed) return text(`Connection "${connection_name}" not found.`); + await notifyReload(); + return text(`Connection "${connection_name}" removed.`); + }, + ); + + // Desktop-only tools: open table and execute-and-show require the Tauri bridge + if (!isWebMode) { + server.tool( + "dbx_open_table", + "Open a table in DBX desktop app UI. Requires DBX to be running.", + { + connection_name: z.string().describe("Name of the DBX connection"), + table: z.string().describe("Table name to open"), + database: z.string().optional().describe("Database name"), + schema: z.string().optional().describe("Schema name"), + }, + async ({ connection_name, table, database, schema }) => { + return bridgeRequest("/open-table", { connection_name, table, database, schema }, `Opened ${table} in DBX`); + }, ); server.tool( - "dbx_execute_and_show", - "Execute a SQL query in DBX desktop app UI and show results there. Requires DBX to be running.", - { - connection_name: z.string().describe("Name of the DBX connection"), - sql: z.string().describe("SQL query to execute"), - database: z.string().optional().describe("Database name"), - }, - async ({ connection_name, sql, database }) => { - const config = await backend.findConnection(connection_name); - const safetyOptions = sqlSafetyFromEnv(); - if (config?.db_type === "mongodb") { - const aggregate = parseMongoAggregateCommand(sql); - if (aggregate) { - const safety = evaluateMongoAggregateSafety(aggregate, safetyOptions); + "dbx_execute_and_show", + "Execute a SQL query in DBX desktop app UI and show results there. Requires DBX to be running.", + { + connection_name: z.string().describe("Name of the DBX connection"), + sql: z.string().describe("SQL query to execute"), + database: z.string().optional().describe("Database name"), + }, + async ({ connection_name, sql, database }) => { + const config = await backend.findConnection(connection_name); + const safetyOptions = sqlSafetyFromEnv(); + if (config?.db_type === "mongodb") { + const aggregate = parseMongoAggregateCommand(sql); + if (aggregate) { + const safety = evaluateMongoAggregateSafety(aggregate, safetyOptions); + if (!safety.allowed) return text(`Query blocked: ${safety.reason}`); + } + } else { + const safety = evaluateSqlSafety(sql, safetyOptions); if (!safety.allowed) return text(`Query blocked: ${safety.reason}`); } - } else { - const safety = evaluateSqlSafety(sql, safetyOptions); - if (!safety.allowed) return text(`Query blocked: ${safety.reason}`); - } - // MongoDB shell commands bypass the SQL safety evaluator; pass MCP - // safety flags to the desktop executor for command-aware gating. - return bridgeRequest( - "/execute-query", - { connection_name, sql, database, allow_writes: safetyOptions.allowWrites, allow_dangerous: safetyOptions.allowDangerous }, - "Query sent to DBX", - ); - }, + // MongoDB shell commands bypass the SQL safety evaluator; pass MCP + // safety flags to the desktop executor for command-aware gating. + return bridgeRequest( + "/execute-query", + { + connection_name, + sql, + database, + allow_writes: safetyOptions.allowWrites, + allow_dangerous: safetyOptions.allowDangerous, + }, + "Query sent to DBX", + ); + }, ); } diff --git a/packages/node-core/src/diagnostics.ts b/packages/node-core/src/diagnostics.ts index 53664aded7..3955cf3f3f 100644 --- a/packages/node-core/src/diagnostics.ts +++ b/packages/node-core/src/diagnostics.ts @@ -38,6 +38,7 @@ export const BRIDGE_REQUIRED_TYPES = [ "bigquery", "kylin", "sundb", + "xugu", "jdbc", "access", ] as const;