mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-21 06:00:39 +00:00
feat: add XuguDB agent support
This commit is contained in:
@@ -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
|
||||
|
||||
<details>
|
||||
<summary><strong>What databases are supported?</strong></summary>
|
||||
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.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
|
||||
+2
-2
@@ -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 和
|
||||
|
||||
<details>
|
||||
<summary><strong>支持哪些数据库?</strong></summary>
|
||||
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 连接。
|
||||
</details>
|
||||
|
||||
<details>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 9.0 KiB |
@@ -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<string, string> = {
|
||||
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)" },
|
||||
|
||||
@@ -62,6 +62,7 @@ const assetIcons: Record<string, string> = {
|
||||
tdsql: "tdsql.webp",
|
||||
polardb: "polardb.webp",
|
||||
greatsql: "greatsql.webp",
|
||||
xugu: "xugu.png",
|
||||
iris: "iris.png",
|
||||
};
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ const SCHEME_PROFILES: Record<string, ConnectionProfile> = {
|
||||
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 },
|
||||
};
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ export const SCHEMA_AWARE_TYPES = new Set<DatabaseType>([
|
||||
"trino",
|
||||
"db2",
|
||||
"tdengine",
|
||||
"xugu",
|
||||
"iris",
|
||||
"duckdb",
|
||||
]);
|
||||
@@ -98,6 +99,7 @@ export const DATABASE_SEARCH_SUPPORTED_TYPES = new Set<DatabaseType>([
|
||||
"kylin",
|
||||
"sundb",
|
||||
"tdengine",
|
||||
"xugu",
|
||||
"iris",
|
||||
]);
|
||||
|
||||
@@ -206,6 +208,7 @@ export const TREE_SCHEMA_TYPES = new Set<DatabaseType>([
|
||||
"trino",
|
||||
"h2",
|
||||
"tdengine",
|
||||
"xugu",
|
||||
"iris",
|
||||
"duckdb",
|
||||
]);
|
||||
@@ -243,6 +246,7 @@ export const AGENT_DRIVER_TYPES = new Set<DatabaseType>([
|
||||
"sundb",
|
||||
"gaussdb",
|
||||
"tdengine",
|
||||
"xugu",
|
||||
"iris",
|
||||
]);
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ export type DatabaseType =
|
||||
| "kylin"
|
||||
| "sundb"
|
||||
| "tdengine"
|
||||
| "xugu"
|
||||
| "iris"
|
||||
| "jdbc";
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -300,6 +300,7 @@ impl AppState {
|
||||
| DatabaseType::Kylin
|
||||
| DatabaseType::Sundb
|
||||
| DatabaseType::Tdengine
|
||||
| DatabaseType::Xugu
|
||||
| DatabaseType::Iris
|
||||
| DatabaseType::Access => {
|
||||
let connect_params =
|
||||
|
||||
@@ -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:<redacted>".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)
|
||||
}
|
||||
|
||||
@@ -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 |
|
||||
|
||||
<Callout type="info">
|
||||
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
|
||||
|
||||
|
||||
@@ -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 |
|
||||
|
||||
<Callout type="info">
|
||||
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
|
||||
|
||||
|
||||
@@ -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 等 |
|
||||
|
||||
<Callout type="info">
|
||||
原生驱动安装后立即可用。Agent 驱动需要一次性下载 JDBC 驱动和 JRE 设置,首次创建连接时 DBX 会自动处理。
|
||||
|
||||
@@ -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 |
|
||||
|
||||
<Callout type="info">
|
||||
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.
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ const expected: Record<string, string> = {
|
||||
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",
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
+189
-180
@@ -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<ConnectionConfig, "id">);
|
||||
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<string, number> = { 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<ConnectionConfig, "id">);
|
||||
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",
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ export const BRIDGE_REQUIRED_TYPES = [
|
||||
"bigquery",
|
||||
"kylin",
|
||||
"sundb",
|
||||
"xugu",
|
||||
"jdbc",
|
||||
"access",
|
||||
] as const;
|
||||
|
||||
Reference in New Issue
Block a user