diff --git a/README.md b/README.md
index e09ef55bc8..fe70bbbd7d 100644
--- a/README.md
+++ b/README.md
@@ -33,6 +33,7 @@
+
@@ -85,7 +86,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, 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.
+MySQL, PostgreSQL, SQLite, Redis, MongoDB, DuckDB, ClickHouse, SQL Server, Oracle, Elasticsearch, MariaDB, TiDB, OceanBase, openGauss, GaussDB, KWDB, 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
@@ -293,7 +294,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, 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.
+MySQL, PostgreSQL, SQLite, Redis, MongoDB, DuckDB, ClickHouse, SQL Server, Oracle, Elasticsearch, MariaDB, TiDB, OceanBase, openGauss, GaussDB, KWDB, 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 56dcfc0074..d8245a7410 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -33,6 +33,7 @@
+
@@ -85,7 +86,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、虚谷 XuguDB、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、KWDB、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。
### 查询编辑器
@@ -293,7 +294,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、虚谷 XuguDB、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、KWDB、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/kwdb.svg b/apps/desktop/public/icons/database/kwdb.svg
new file mode 100644
index 0000000000..487775987f
--- /dev/null
+++ b/apps/desktop/public/icons/database/kwdb.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/desktop/src/components/connection/ConnectionDialog.vue b/apps/desktop/src/components/connection/ConnectionDialog.vue
index 3a952293df..ff6f8ab153 100644
--- a/apps/desktop/src/components/connection/ConnectionDialog.vue
+++ b/apps/desktop/src/components/connection/ConnectionDialog.vue
@@ -281,6 +281,7 @@ const driverProfiles: Record<
icon: "opengauss",
},
gaussdb: { type: "gaussdb", port: 5432, user: "gaussdb", label: "GaussDB", icon: "gaussdb" },
+ kwdb: { type: "kwdb", port: 26257, user: "root", label: "KWDB", icon: "kwdb" },
kingbase: { type: "kingbase", port: 54321, user: "system", label: "KingBase", icon: "kingbase" },
highgo: { type: "highgo", port: 5866, user: "highgo", label: "瀚高 HighGo", icon: "highgo" },
yashandb: { type: "yashandb", port: 1688, user: "sys", label: "崖山 YashanDB", icon: "yashandb" },
@@ -532,6 +533,7 @@ const sshPathSegments = computed(() => {
function defaultDatabaseForProfile() {
if (form.value.db_type === "redshift") return "dev";
if (form.value.db_type === "gaussdb") return "postgres";
+ if (form.value.db_type === "kwdb") return "defaultdb";
if (selectedType.value === "cockroachdb") return "defaultdb";
if (form.value.db_type === "highgo") return "highgo";
if (form.value.db_type === "yashandb") return "yasdb";
@@ -580,6 +582,7 @@ const iconTypeMap: Record = {
gbase: "gbase",
opengauss: "opengauss",
gaussdb: "gaussdb",
+ kwdb: "kwdb",
kingbase: "kingbase",
highgo: "highgo",
yashandb: "yashandb",
@@ -625,6 +628,7 @@ const dbOptions = [
{ value: "mariadb", label: "MariaDB" },
{ value: "dm", label: "DM (Dameng)" },
{ value: "gaussdb", label: "GaussDB" },
+ { value: "kwdb", label: "KWDB" },
{ value: "tidb", label: "TiDB" },
{ value: "oceanbase", label: "OceanBase" },
{ value: "goldendb", label: "GoldenDB" },
@@ -710,6 +714,7 @@ const tlsCapableDatabaseTypes = new Set([
"postgres",
"redshift",
"gaussdb",
+ "kwdb",
"opengauss",
"redis",
"clickhouse",
@@ -742,7 +747,7 @@ const mysqlClientKeyPath = computed({
form.value.url_params = setUrlParam(next, "ssl-key", value);
},
});
-const nativePostgresTlsDatabaseTypes = new Set(["postgres", "redshift", "gaussdb", "opengauss"]);
+const nativePostgresTlsDatabaseTypes = new Set(["postgres", "redshift", "gaussdb", "kwdb", "opengauss"]);
const supportsPostgresTlsOptions = computed(() => nativePostgresTlsDatabaseTypes.has(form.value.db_type));
const postgresTlsMode = computed({
get: () => {
diff --git a/apps/desktop/src/components/icons/DatabaseIcon.vue b/apps/desktop/src/components/icons/DatabaseIcon.vue
index 236f0edcc1..b460d8502b 100644
--- a/apps/desktop/src/components/icons/DatabaseIcon.vue
+++ b/apps/desktop/src/components/icons/DatabaseIcon.vue
@@ -29,6 +29,7 @@ const assetIcons: Record = {
oceanbase: "oceanbase",
opengauss: "opengauss",
gaussdb: "gaussdb",
+ kwdb: "kwdb",
kingbase: "kingbase",
highgo: "highgo.png",
goldendb: "goldendb.png",
diff --git a/apps/desktop/src/components/layout/ContentArea.vue b/apps/desktop/src/components/layout/ContentArea.vue
index 7425c62612..f0c206a417 100644
--- a/apps/desktop/src/components/layout/ContentArea.vue
+++ b/apps/desktop/src/components/layout/ContentArea.vue
@@ -156,6 +156,7 @@ const activeSqlFormatDialect = computed(() => {
case "mysql":
return "mysql";
case "postgres":
+ case "kwdb":
return "postgres";
case "sqlite":
case "rqlite":
@@ -168,7 +169,8 @@ const activeSqlFormatDialect = computed(() => {
});
const editorDialect = computed<"mysql" | "postgres" | "sqlserver">(() => {
- if (activeEffectiveDatabaseType.value === "postgres") return "postgres";
+ if (activeEffectiveDatabaseType.value === "postgres" || activeEffectiveDatabaseType.value === "kwdb")
+ return "postgres";
if (activeEffectiveDatabaseType.value === "sqlserver") return "sqlserver";
return "mysql";
});
diff --git a/apps/desktop/src/components/objects/ObjectBrowser.vue b/apps/desktop/src/components/objects/ObjectBrowser.vue
index 3f263eafeb..40317da3d5 100644
--- a/apps/desktop/src/components/objects/ObjectBrowser.vue
+++ b/apps/desktop/src/components/objects/ObjectBrowser.vue
@@ -181,6 +181,7 @@ const sourceDialect = computed<"mysql" | "postgres" | "sqlserver">(() => {
if (
effectiveDatabaseType.value === "postgres" ||
effectiveDatabaseType.value === "gaussdb" ||
+ effectiveDatabaseType.value === "kwdb" ||
effectiveDatabaseType.value === "opengauss"
)
return "postgres";
@@ -192,10 +193,12 @@ const sourceFormatDialect = computed(() => {
case "mysql":
case "postgres":
case "sqlite":
- case "rqlite":
case "sqlserver":
return effectiveDatabaseType.value;
+ case "rqlite":
+ return "sqlite";
case "gaussdb":
+ case "kwdb":
case "opengauss":
return "postgres";
default:
diff --git a/apps/desktop/src/components/structure/TableStructureEditor.vue b/apps/desktop/src/components/structure/TableStructureEditor.vue
index d75dd5343c..51a00e94c8 100644
--- a/apps/desktop/src/components/structure/TableStructureEditor.vue
+++ b/apps/desktop/src/components/structure/TableStructureEditor.vue
@@ -307,6 +307,7 @@ function isPostgresIdentityType(dbType: string | undefined): boolean {
return (
dbType === "postgres" ||
dbType === "gaussdb" ||
+ dbType === "kwdb" ||
dbType === "opengauss" ||
dbType === "highgo" ||
dbType === "vastbase" ||
diff --git a/apps/desktop/src/lib/connectionPresentation.ts b/apps/desktop/src/lib/connectionPresentation.ts
index 0e75534d22..1de3c97e77 100644
--- a/apps/desktop/src/lib/connectionPresentation.ts
+++ b/apps/desktop/src/lib/connectionPresentation.ts
@@ -92,6 +92,7 @@ export function connectionUrlPlaceholder(dbType: DatabaseType): string {
case "postgres":
case "gaussdb":
+ case "kwdb":
case "yashandb":
case "redshift":
return "postgresql://user:password@host:port/database";
diff --git a/apps/desktop/src/lib/connectionUrl.ts b/apps/desktop/src/lib/connectionUrl.ts
index 26504da5f3..e7921c37f7 100644
--- a/apps/desktop/src/lib/connectionUrl.ts
+++ b/apps/desktop/src/lib/connectionUrl.ts
@@ -41,6 +41,7 @@ const SCHEME_PROFILES: Record = {
dm: { type: "dameng", profile: "dm", label: "DM (Dameng)", defaultPort: 5236 },
dameng: { type: "dameng", profile: "dm", label: "DM (Dameng)", defaultPort: 5236 },
gaussdb: { type: "gaussdb", profile: "gaussdb", label: "GaussDB", defaultPort: 5432 },
+ kwdb: { type: "kwdb", profile: "kwdb", label: "KWDB", defaultPort: 26257 },
gbase: { type: "gbase", profile: "gbase", label: "GBase", defaultPort: 5258 },
"gbasedbt-sqli": { type: "gbase", profile: "gbase8s", label: "GBase 8s", defaultPort: 9088 },
yashandb: { type: "yashandb", profile: "yashandb", label: "YashanDB", defaultPort: 1688 },
@@ -143,7 +144,7 @@ function urlParamsRequireTls(dbType: DatabaseType, params: string): boolean {
return sslMode === "required" || sslMode === "require" || sslMode === "verify_ca" || sslMode === "verify_identity";
}
- if (dbType === "postgres" || dbType === "redshift") {
+ if (dbType === "postgres" || dbType === "redshift" || dbType === "kwdb") {
const sslMode = (queryParamValue(params, "sslmode") || "").toLowerCase();
return sslMode === "require" || sslMode === "verify-ca" || sslMode === "verify-full";
}
diff --git a/apps/desktop/src/lib/databaseCapabilitySets.ts b/apps/desktop/src/lib/databaseCapabilitySets.ts
index c3500588d7..723d93849d 100644
--- a/apps/desktop/src/lib/databaseCapabilitySets.ts
+++ b/apps/desktop/src/lib/databaseCapabilitySets.ts
@@ -7,6 +7,7 @@ export const SCHEMA_AWARE_TYPES = new Set([
"redshift",
"dameng",
"gaussdb",
+ "kwdb",
"kingbase",
"highgo",
"vastbase",
@@ -43,6 +44,7 @@ export const DIAGRAM_SUPPORTED_TYPES = new Set([
"redshift",
"dameng",
"gaussdb",
+ "kwdb",
"kingbase",
"highgo",
"vastbase",
@@ -75,6 +77,7 @@ export const DATABASE_SEARCH_SUPPORTED_TYPES = new Set([
"clickhouse",
"dameng",
"gaussdb",
+ "kwdb",
"kingbase",
"highgo",
"vastbase",
@@ -120,6 +123,7 @@ export const TABLE_IMPORT_SUPPORTED_TYPES = new Set([
"redshift",
"dameng",
"gaussdb",
+ "kwdb",
"kingbase",
"highgo",
"vastbase",
@@ -142,6 +146,7 @@ export const TABLE_STRUCTURE_SUPPORTED_TYPES = new Set([
"redshift",
"dameng",
"gaussdb",
+ "kwdb",
"kingbase",
"highgo",
"vastbase",
@@ -162,6 +167,7 @@ export const CREATE_DATABASE_SUPPORTED_TYPES = new Set([
"oracle",
"dameng",
"gaussdb",
+ "kwdb",
"opengauss",
"oceanbase-oracle",
"doris",
@@ -179,6 +185,7 @@ export const FIELD_LINEAGE_SUPPORTED_TYPES = new Set([
"redshift",
"dameng",
"gaussdb",
+ "kwdb",
"opengauss",
"oceanbase-oracle",
]);
@@ -199,6 +206,7 @@ export const TREE_SCHEMA_TYPES = new Set([
"sqlserver",
"db2",
"gaussdb",
+ "kwdb",
"kingbase",
"highgo",
"vastbase",
@@ -222,7 +230,7 @@ export const TREE_SCHEMA_TYPES = new Set([
export const DATABASE_OBJECT_TREE_TYPES = new Set(["jdbc"]);
-export const PG_LIKE_STRUCTURE_TYPES = new Set(["postgres", "redshift", "gaussdb", "opengauss"]);
+export const PG_LIKE_STRUCTURE_TYPES = new Set(["postgres", "redshift", "gaussdb", "kwdb", "opengauss"]);
export const AGENT_DRIVER_TYPES = new Set([
"dameng",
@@ -270,6 +278,7 @@ export const TRANSFER_SQL_TYPES = new Set([
"duckdb",
"dameng",
"gaussdb",
+ "kwdb",
"opengauss",
"oceanbase-oracle",
]);
@@ -284,6 +293,7 @@ export const DIAGRAM_SQL_TYPES = new Set([
"redshift",
"dameng",
"gaussdb",
+ "kwdb",
"opengauss",
"oceanbase-oracle",
]);
diff --git a/apps/desktop/src/lib/databaseTableDataCapabilities.ts b/apps/desktop/src/lib/databaseTableDataCapabilities.ts
index 7aa80195fd..b2ce37035d 100644
--- a/apps/desktop/src/lib/databaseTableDataCapabilities.ts
+++ b/apps/desktop/src/lib/databaseTableDataCapabilities.ts
@@ -58,6 +58,7 @@ const NAVICAT_STYLE_TABLE_DATA_TYPES = new Set([
"redshift",
"dameng",
"gaussdb",
+ "kwdb",
"kingbase",
"highgo",
"vastbase",
diff --git a/apps/desktop/src/lib/dbeaverImport.ts b/apps/desktop/src/lib/dbeaverImport.ts
index a966d3510e..d3cb88a528 100644
--- a/apps/desktop/src/lib/dbeaverImport.ts
+++ b/apps/desktop/src/lib/dbeaverImport.ts
@@ -48,6 +48,7 @@ const profileMap: Record = {
dameng: { dbType: "dameng", profile: "dm", label: "DM (Dameng)", port: 5236, user: "SYSDBA" },
dm: { dbType: "dameng", profile: "dm", label: "DM (Dameng)", port: 5236, user: "SYSDBA" },
gaussdb: { dbType: "gaussdb", profile: "gaussdb", label: "GaussDB", port: 5432, user: "gaussdb" },
+ kwdb: { dbType: "kwdb", profile: "kwdb", label: "KWDB", port: 26257, user: "root" },
opengauss: { dbType: "gaussdb", profile: "opengauss", label: "openGauss", port: 5432, user: "gaussdb" },
};
diff --git a/apps/desktop/src/lib/objectRenameSql.ts b/apps/desktop/src/lib/objectRenameSql.ts
index eb7205fc0d..233daeb42a 100644
--- a/apps/desktop/src/lib/objectRenameSql.ts
+++ b/apps/desktop/src/lib/objectRenameSql.ts
@@ -15,6 +15,7 @@ const postgresLikeRenameTypes = new Set([
"postgres",
"redshift",
"gaussdb",
+ "kwdb",
"kingbase",
"highgo",
"vastbase",
diff --git a/apps/desktop/src/lib/objectSourceEditor.ts b/apps/desktop/src/lib/objectSourceEditor.ts
index e3c9c531b7..7fac0cc876 100644
--- a/apps/desktop/src/lib/objectSourceEditor.ts
+++ b/apps/desktop/src/lib/objectSourceEditor.ts
@@ -19,6 +19,7 @@ const postgresLikeRoutineRenameTypes = new Set([
"postgres",
"redshift",
"gaussdb",
+ "kwdb",
"kingbase",
"highgo",
"vastbase",
diff --git a/apps/desktop/src/lib/tableStructureCapabilities.ts b/apps/desktop/src/lib/tableStructureCapabilities.ts
index 2fb2eee386..d388f7dc4b 100644
--- a/apps/desktop/src/lib/tableStructureCapabilities.ts
+++ b/apps/desktop/src/lib/tableStructureCapabilities.ts
@@ -216,6 +216,7 @@ const capabilityByType: Partial
sundb: mysqlCapabilities,
postgres: postgresCapabilities,
gaussdb: postgresCapabilities,
+ kwdb: postgresCapabilities,
opengauss: postgresCapabilities,
redshift: redshiftCapabilities,
highgo: postgresCapabilities,
diff --git a/apps/desktop/src/lib/tableStructureEditorState.ts b/apps/desktop/src/lib/tableStructureEditorState.ts
index c5963b5795..f1528643e8 100644
--- a/apps/desktop/src/lib/tableStructureEditorState.ts
+++ b/apps/desktop/src/lib/tableStructureEditorState.ts
@@ -237,6 +237,7 @@ const DATA_TYPE_OPTION_ALIASES: Partial> = {
goldendb: "mysql",
sundb: "mysql",
gaussdb: "postgres",
+ kwdb: "postgres",
opengauss: "postgres",
redshift: "postgres",
highgo: "postgres",
@@ -298,6 +299,7 @@ export function parseExtraToColumnExtra(extra: string | null | undefined, databa
} else if (
databaseType === "postgres" ||
databaseType === "gaussdb" ||
+ databaseType === "kwdb" ||
databaseType === "opengauss" ||
databaseType === "highgo" ||
databaseType === "vastbase" ||
@@ -405,6 +407,7 @@ function isTemporalPrecisionType(dbType: DatabaseType | undefined, baseType: str
return ["time", "datetime", "timestamp"].includes(normalized);
case "postgres":
case "gaussdb":
+ case "kwdb":
case "opengauss":
case "highgo":
case "vastbase":
diff --git a/apps/desktop/src/lib/visibleDatabases.ts b/apps/desktop/src/lib/visibleDatabases.ts
index 914c25eef0..f2694baae2 100644
--- a/apps/desktop/src/lib/visibleDatabases.ts
+++ b/apps/desktop/src/lib/visibleDatabases.ts
@@ -8,6 +8,7 @@ const SYSTEM_DATABASE_RULES: Partial>>
gbase: new Set(["information_schema", "mysql", "performance_schema", "sys"]),
postgres: new Set(["template0", "template1"]),
gaussdb: new Set(["template0", "template1"]),
+ kwdb: new Set(["template0", "template1"]),
opengauss: new Set(["template0", "template1"]),
kingbase: new Set(["template0", "template1"]),
highgo: new Set(["template0", "template1"]),
diff --git a/apps/desktop/src/stores/connectionStore.ts b/apps/desktop/src/stores/connectionStore.ts
index 58982bb259..b98b2f6244 100644
--- a/apps/desktop/src/stores/connectionStore.ts
+++ b/apps/desktop/src/stores/connectionStore.ts
@@ -243,6 +243,7 @@ export const useConnectionStore = defineStore("connection", () => {
redshift: "Redshift",
dameng: "DM (Dameng)",
gaussdb: "GaussDB",
+ kwdb: "KWDB",
kingbase: "KingBase",
highgo: "瀚高 HighGo",
yashandb: "崖山 YashanDB",
@@ -266,6 +267,8 @@ export const useConnectionStore = defineStore("connection", () => {
let dbType = config.db_type;
if ((profile === "gaussdb" || profile === "opengauss") && dbType === "postgres") {
dbType = "gaussdb" as ConnectionConfig["db_type"];
+ } else if (profile === "kwdb" && dbType === "postgres") {
+ dbType = "kwdb" as ConnectionConfig["db_type"];
} else if (profile === "redshift" && dbType === "postgres") {
dbType = "redshift" as ConnectionConfig["db_type"];
} else if (profile === "kingbase" && dbType === "postgres") {
diff --git a/apps/desktop/src/stores/queryStore.ts b/apps/desktop/src/stores/queryStore.ts
index d7ee4f10f8..b2ca689621 100644
--- a/apps/desktop/src/stores/queryStore.ts
+++ b/apps/desktop/src/stores/queryStore.ts
@@ -689,7 +689,7 @@ export const useQueryStore = defineStore("query", () => {
const dbType = conn?.db_type || "";
let schema = analysis.schema || tab.schema;
if (!schema) {
- if (dbType === "postgres") schema = "public";
+ if (dbType === "postgres" || dbType === "kwdb") schema = "public";
else schema = "";
}
const metadataSchema =
diff --git a/apps/desktop/src/types/database.ts b/apps/desktop/src/types/database.ts
index c43b3ea047..168da867f5 100644
--- a/apps/desktop/src/types/database.ts
+++ b/apps/desktop/src/types/database.ts
@@ -19,6 +19,7 @@ export type DatabaseType =
| "highgo"
| "vastbase"
| "goldendb"
+ | "kwdb"
| "yashandb"
| "databricks"
| "saphana"
diff --git a/crates/dbx-core/assets/database-drivers.manifest.json b/crates/dbx-core/assets/database-drivers.manifest.json
index 521c625baa..098691366d 100644
--- a/crates/dbx-core/assets/database-drivers.manifest.json
+++ b/crates/dbx-core/assets/database-drivers.manifest.json
@@ -210,6 +210,16 @@
"skipTcpProbe": false,
"defaultPort": 8000
},
+ {
+ "dbType": "kwdb",
+ "label": "KWDB",
+ "runtimeMode": "native",
+ "mcpMode": "direct",
+ "singleConnectionPool": false,
+ "metadataConnectionScoped": false,
+ "skipTcpProbe": false,
+ "defaultPort": 26257
+ },
{
"dbType": "yashandb",
"label": "崖山 YashanDB",
diff --git a/crates/dbx-core/src/connection.rs b/crates/dbx-core/src/connection.rs
index 7c9ffd3c1b..069d689ae3 100644
--- a/crates/dbx-core/src/connection.rs
+++ b/crates/dbx-core/src/connection.rs
@@ -329,9 +329,11 @@ impl AppState {
};
PoolKind::Mysql(pool, MysqlMode::Bare)
}
- DatabaseType::Postgres | DatabaseType::Redshift | DatabaseType::Gaussdb | DatabaseType::OpenGauss => {
- PoolKind::Postgres(db::postgres::connect(&url, connect_timeout).await?)
- }
+ DatabaseType::Postgres
+ | DatabaseType::Redshift
+ | DatabaseType::Gaussdb
+ | DatabaseType::Kwdb
+ | DatabaseType::OpenGauss => PoolKind::Postgres(db::postgres::connect(&url, connect_timeout).await?),
DatabaseType::Sqlite => {
let extensions = db::sqlite::sqlite_extension_specs_from_url_params(db_config.url_params.as_deref())
.into_iter()
@@ -1011,9 +1013,10 @@ fn external_driver_connect_timeout(config: &ConnectionConfig) -> std::time::Dura
fn native_postgres_url_config(config: &ConnectionConfig) -> Option {
match config.db_type {
- DatabaseType::Gaussdb | DatabaseType::OpenGauss => {
+ DatabaseType::Gaussdb | DatabaseType::Kwdb | DatabaseType::OpenGauss => {
let mut normalized = config.clone();
- if config.db_type == DatabaseType::Gaussdb {
+ normalized.database = normalized.effective_database().map(str::to_string);
+ if matches!(config.db_type, DatabaseType::Gaussdb | DatabaseType::Kwdb) {
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() {
@@ -1124,6 +1127,7 @@ mod tests {
use crate::agent_manager::{AgentState, JavaRuntimeConfig, JavaRuntimeMode, DEFAULT_JRE_KEY};
use crate::db;
use crate::models::connection::{default_connect_timeout_secs, ConnectionConfig, DatabaseType, ProxyType};
+ use crate::query;
use crate::schema;
use crate::storage::Storage;
@@ -1613,6 +1617,39 @@ mod tests {
);
}
+ #[test]
+ fn kwdb_endpoint_url_uses_postgres_scheme_for_native_driver() {
+ let mut config = mysql_config(None);
+ config.db_type = DatabaseType::Kwdb;
+ config.username = "root".to_string();
+ config.password = "secret".to_string();
+ config.port = 26257;
+
+ assert_eq!(
+ connection_url_for_endpoint(&config, &config.host, config.port),
+ "postgres://root:secret@127.0.0.1:26257/defaultdb?sslmode=disable"
+ );
+ assert_eq!(
+ redacted_connection_url_for_endpoint(&config, &config.host, config.port),
+ "postgres://127.0.0.1:26257/defaultdb?sslmode=disable"
+ );
+ }
+
+ #[test]
+ fn kwdb_endpoint_url_keeps_explicit_sslmode() {
+ let mut config = mysql_config(None);
+ config.db_type = DatabaseType::Kwdb;
+ config.username = "root".to_string();
+ config.password = "secret".to_string();
+ config.port = 26257;
+ config.url_params = Some("sslmode=require&application_name=dbx".to_string());
+
+ assert_eq!(
+ connection_url_for_endpoint(&config, &config.host, config.port),
+ "postgres://root:secret@127.0.0.1:26257/defaultdb?sslmode=require&application_name=dbx"
+ );
+ }
+
#[test]
fn opengauss_endpoint_url_uses_postgres_scheme_for_native_driver() {
let mut config = mysql_config(Some("postgres"));
@@ -1739,6 +1776,7 @@ mod tests {
DatabaseType::ClickHouse,
DatabaseType::SqlServer,
DatabaseType::Elasticsearch,
+ DatabaseType::Kwdb,
] {
let mut config = mysql_config(Some("app"));
config.db_type = db_type;
@@ -1933,4 +1971,121 @@ mod tests {
))
.await;
}
+
+ #[tokio::test]
+ #[ignore = "requires a reachable KWDB instance via environment variables"]
+ async fn live_kwdb_native_connection_succeeds() {
+ let host = std::env::var("DBX_TEST_KWDB_HOST").expect("DBX_TEST_KWDB_HOST not set");
+ let port = std::env::var("DBX_TEST_KWDB_PORT")
+ .expect("DBX_TEST_KWDB_PORT not set")
+ .parse::()
+ .expect("DBX_TEST_KWDB_PORT should be a u16");
+ let username = std::env::var("DBX_TEST_KWDB_USER").unwrap_or_else(|_| "root".to_string());
+ let password = std::env::var("DBX_TEST_KWDB_PASSWORD").unwrap_or_default();
+ let database = std::env::var("DBX_TEST_KWDB_DATABASE").unwrap_or_else(|_| "defaultdb".to_string());
+ let url_params = std::env::var("DBX_TEST_KWDB_URL_PARAMS").unwrap_or_else(|_| "sslmode=disable".to_string());
+
+ let mut config = mysql_config(Some(&database));
+ config.id = "kwdb-live".to_string();
+ config.db_type = DatabaseType::Kwdb;
+ config.host = host;
+ config.port = port;
+ config.username = username;
+ config.password = password;
+ config.url_params = Some(url_params);
+
+ let (state, dir) = test_app_state().await;
+ state.configs.write().await.insert(config.id.clone(), config);
+ let pool_key = state.get_or_create_pool("kwdb-live", None).await.unwrap();
+ let pool = {
+ let connections = state.connections.read().await;
+ match connections.get(&pool_key).expect("KWDB pool should be created") {
+ PoolKind::Postgres(pool) => pool.clone(),
+ _ => panic!("KWDB should use the PostgreSQL pool path"),
+ }
+ };
+ let result = query::execute_sql_statement(
+ &state,
+ "kwdb-live",
+ &database,
+ "SELECT current_database(), current_schema()",
+ None,
+ None,
+ )
+ .await
+ .unwrap_or_else(|err| panic!("failed to query live KWDB: {err}"));
+ assert_eq!(result.rows.len(), 1);
+ let database_column_index = result
+ .columns
+ .iter()
+ .position(|column| column == "current_database")
+ .expect("current_database column should be present");
+ assert_eq!(result.rows[0].get(database_column_index).and_then(|value| value.as_str()), Some(database.as_str()));
+ let databases = schema::list_databases_core(&state, "kwdb-live").await.unwrap();
+ assert!(databases.iter().any(|database| database.name == "defaultdb"));
+ let test_schema = "dbx_kwdb_live";
+ db::postgres::execute_query(&pool, &format!("DROP SCHEMA IF EXISTS {test_schema} CASCADE"))
+ .await
+ .unwrap_or_else(|err| panic!("failed to clean KWDB test schema: {err}"));
+ query::execute_sql_statement(
+ &state,
+ "kwdb-live",
+ &database,
+ &format!("CREATE SCHEMA {test_schema}"),
+ None,
+ None,
+ )
+ .await
+ .unwrap_or_else(|err| panic!("failed to create KWDB test schema: {err}"));
+ query::execute_sql_statement(
+ &state,
+ "kwdb-live",
+ &database,
+ "CREATE TABLE devices (id INT PRIMARY KEY, name STRING, active BOOL)",
+ Some(test_schema),
+ None,
+ )
+ .await
+ .unwrap_or_else(|err| panic!("failed to create KWDB test table: {err}"));
+ query::execute_sql_statement(
+ &state,
+ "kwdb-live",
+ &database,
+ "INSERT INTO devices (id, name, active) VALUES (1, 'meter-a', true)",
+ Some(test_schema),
+ None,
+ )
+ .await
+ .unwrap_or_else(|err| panic!("failed to insert KWDB test row: {err}"));
+ let query_result = query::execute_sql_statement(
+ &state,
+ "kwdb-live",
+ &database,
+ "SELECT name, active FROM devices WHERE id = 1",
+ Some(test_schema),
+ None,
+ )
+ .await
+ .unwrap_or_else(|err| panic!("failed to query KWDB test row: {err}"));
+ assert_eq!(query_result.rows.len(), 1);
+ assert_eq!(query_result.rows[0].first().and_then(|value| value.as_str()), Some("meter-a"));
+
+ let schemas = schema::list_schemas_core(&state, "kwdb-live", &database).await.unwrap();
+ assert!(schemas.iter().any(|schema| schema == test_schema));
+ let tables = schema::list_tables_core(&state, "kwdb-live", &database, test_schema, None, None).await.unwrap();
+ assert!(tables.iter().any(|table| table.name == "devices" && table.table_type == "BASE TABLE"));
+ let columns = schema::get_columns_core(&state, "kwdb-live", &database, test_schema, "devices").await.unwrap();
+ let id_column = columns.iter().find(|column| column.name == "id").expect("id column should be listed");
+ assert!(id_column.data_type.to_lowercase().contains("int"));
+ let name_column = columns.iter().find(|column| column.name == "name").expect("name column should be listed");
+ assert!(name_column.data_type.to_lowercase().contains("text"));
+ let active_column =
+ columns.iter().find(|column| column.name == "active").expect("active column should be listed");
+ assert!(active_column.data_type.to_lowercase().contains("bool"));
+ db::postgres::execute_query(&pool, &format!("DROP SCHEMA {test_schema} CASCADE"))
+ .await
+ .unwrap_or_else(|err| panic!("failed to drop KWDB test schema: {err}"));
+ pool.close();
+ let _ = std::fs::remove_dir_all(dir);
+ }
}
diff --git a/crates/dbx-core/src/data_grid_sql.rs b/crates/dbx-core/src/data_grid_sql.rs
index 7be2821414..476b98fc52 100644
--- a/crates/dbx-core/src/data_grid_sql.rs
+++ b/crates/dbx-core/src/data_grid_sql.rs
@@ -1242,6 +1242,7 @@ fn uses_keyless_row_predicate(database_type: Option) -> bool {
| DatabaseType::Redshift
| DatabaseType::Dameng
| DatabaseType::Gaussdb
+ | DatabaseType::Kwdb
| DatabaseType::Kingbase
| DatabaseType::Highgo
| DatabaseType::Vastbase
diff --git a/crates/dbx-core/src/db_admin_sql.rs b/crates/dbx-core/src/db_admin_sql.rs
index c96c98d64d..b8b664b526 100644
--- a/crates/dbx-core/src/db_admin_sql.rs
+++ b/crates/dbx-core/src/db_admin_sql.rs
@@ -174,6 +174,7 @@ pub fn build_drop_table_child_object_sql(options: DropTableChildObjectSqlOptions
Some(
DatabaseType::Postgres
| DatabaseType::Gaussdb
+ | DatabaseType::Kwdb
| DatabaseType::OpenGauss
| DatabaseType::Highgo
| DatabaseType::Vastbase
@@ -203,6 +204,7 @@ pub fn build_drop_table_child_object_sql(options: DropTableChildObjectSqlOptions
Some(
DatabaseType::Postgres
| DatabaseType::Gaussdb
+ | DatabaseType::Kwdb
| DatabaseType::OpenGauss
| DatabaseType::Highgo
| DatabaseType::Vastbase
@@ -254,7 +256,7 @@ pub fn build_create_schema_sql(options: SchemaNameSqlOptions) -> String {
pub fn build_drop_schema_sql(options: SchemaNameSqlOptions) -> String {
let schema = quote_table_identifier(options.database_type, &options.name);
- if matches!(options.database_type, Some(DatabaseType::Postgres | DatabaseType::Gaussdb)) {
+ if matches!(options.database_type, Some(DatabaseType::Postgres | DatabaseType::Gaussdb | DatabaseType::Kwdb)) {
format!("DROP SCHEMA {schema} CASCADE;")
} else {
format!("DROP SCHEMA {schema};")
@@ -359,6 +361,7 @@ fn is_postgres_like_rename(database_type: DatabaseType) -> bool {
DatabaseType::Postgres
| DatabaseType::Redshift
| DatabaseType::Gaussdb
+ | DatabaseType::Kwdb
| DatabaseType::Kingbase
| DatabaseType::Highgo
| DatabaseType::Vastbase
@@ -372,7 +375,11 @@ fn is_oracle_like_rename(database_type: DatabaseType) -> bool {
fn is_postgres_like_structure_copy(database_type: DatabaseType) -> bool {
matches!(
database_type,
- DatabaseType::Postgres | DatabaseType::Redshift | DatabaseType::Gaussdb | DatabaseType::OpenGauss
+ DatabaseType::Postgres
+ | DatabaseType::Redshift
+ | DatabaseType::Gaussdb
+ | DatabaseType::Kwdb
+ | DatabaseType::OpenGauss
)
}
@@ -564,7 +571,7 @@ mod tests {
);
assert_eq!(
build_drop_schema_sql(SchemaNameSqlOptions {
- database_type: Some(DatabaseType::Gaussdb),
+ database_type: Some(DatabaseType::Kwdb),
name: "analytics".to_string(),
}),
"DROP SCHEMA \"analytics\" CASCADE;"
@@ -654,7 +661,7 @@ mod tests {
);
assert_eq!(
build_duplicate_table_structure_sql(DuplicateTableStructureSqlOptions {
- database_type: Some(DatabaseType::Postgres),
+ database_type: Some(DatabaseType::Kwdb),
schema: Some("public".to_string()),
source_name: "users".to_string(),
target_name: "users_copy".to_string(),
diff --git a/crates/dbx-core/src/models/connection.rs b/crates/dbx-core/src/models/connection.rs
index 1ebc236782..399b5a0c95 100644
--- a/crates/dbx-core/src/models/connection.rs
+++ b/crates/dbx-core/src/models/connection.rs
@@ -193,6 +193,7 @@ pub enum DatabaseType {
Vastbase,
Goldendb,
Gaussdb,
+ Kwdb,
Yashandb,
Databricks,
#[serde(rename = "saphana")]
@@ -304,6 +305,7 @@ impl ConnectionConfig {
DatabaseType::ClickHouse => Some("default"),
DatabaseType::Rqlite => Some("main"),
DatabaseType::Gaussdb | DatabaseType::OpenGauss => Some("postgres"),
+ DatabaseType::Kwdb => Some("defaultdb"),
DatabaseType::Kingbase | DatabaseType::Vastbase => Some("postgres"),
DatabaseType::Highgo => Some("highgo"),
DatabaseType::Yashandb => Some("yasdb"),
@@ -424,6 +426,7 @@ impl ConnectionConfig {
DatabaseType::Vastbase => format!("vastbase://{host}:{port}{db_part}"),
DatabaseType::Goldendb => format!("goldendb://{host}:{port}{db_part}"),
DatabaseType::Gaussdb => format!("gaussdb://{host}:{port}{db_part}"),
+ DatabaseType::Kwdb => format!("kwdb://{host}:{port}{db_part}"),
DatabaseType::Yashandb => format!("yashandb://{host}:{port}{db_part}"),
DatabaseType::Databricks => format!("databricks://{host}:{port}{db_part}"),
DatabaseType::SapHana => format!("saphana://{host}:{port}{db_part}"),
@@ -549,6 +552,9 @@ impl ConnectionConfig {
DatabaseType::Gaussdb => {
format!("gaussdb://{}:{}@{host}:{port}{db_part}", username, password)
}
+ DatabaseType::Kwdb => {
+ format!("kwdb://{}:{}@{host}:{port}{db_part}", username, password)
+ }
DatabaseType::Yashandb => {
format!("yashandb://{}:{}@{host}:{port}{db_part}", username, password)
}
@@ -1618,6 +1624,14 @@ mod tests {
assert_eq!(config.connection_url(), "gaussdb://gaussdb:secret@10.1.2.3:2883/postgres");
}
+ #[test]
+ fn kwdb_url_defaults_to_defaultdb_database() {
+ let mut config = mysql_config("root", "secret", None);
+ config.db_type = DatabaseType::Kwdb;
+
+ assert_eq!(config.connection_url(), "kwdb://root:secret@10.1.2.3:2883/defaultdb");
+ }
+
#[test]
fn yashandb_url_defaults_to_yasdb_database() {
let mut config = mysql_config("sys", "secret", None);
diff --git a/crates/dbx-core/src/object_source_sql.rs b/crates/dbx-core/src/object_source_sql.rs
index b3cecf3c2e..baddb7a21e 100644
--- a/crates/dbx-core/src/object_source_sql.rs
+++ b/crates/dbx-core/src/object_source_sql.rs
@@ -125,8 +125,10 @@ pub fn build_executable_object_source_statements(input: EditableObjectSourceSqlI
return Ok(vec![replace_sqlserver_create_with_create_or_alter(source)]);
}
- if matches!(input.database_type, DatabaseType::Postgres | DatabaseType::Gaussdb | DatabaseType::OpenGauss)
- && input.object_type == ObjectSourceKind::View
+ if matches!(
+ input.database_type,
+ DatabaseType::Postgres | DatabaseType::Gaussdb | DatabaseType::Kwdb | DatabaseType::OpenGauss
+ ) && input.object_type == ObjectSourceKind::View
{
return Ok(vec![format!(
"CREATE OR REPLACE VIEW {} AS\n{}",
@@ -211,6 +213,7 @@ fn is_postgres_like(database_type: DatabaseType) -> bool {
DatabaseType::Postgres
| DatabaseType::Redshift
| DatabaseType::Gaussdb
+ | DatabaseType::Kwdb
| DatabaseType::OpenGauss
| DatabaseType::Kingbase
| DatabaseType::Highgo
@@ -447,19 +450,21 @@ mod tests {
}
#[test]
- fn opengauss_view_body_opens_as_create_or_replace_view() {
- let sql = build_executable_object_source_sql(EditableObjectSourceSqlInput {
- database_type: DatabaseType::OpenGauss,
- object_type: ObjectSourceKind::View,
- schema: Some("public".to_string()),
- name: "active users".to_string(),
- source: " SELECT id, name FROM users WHERE active ".to_string(),
- })
- .unwrap();
- assert_eq!(
- sql,
- "CREATE OR REPLACE VIEW \"public\".\"active users\" AS\nSELECT id, name FROM users WHERE active;"
- );
+ fn postgres_compatible_view_body_opens_as_create_or_replace_view() {
+ for database_type in [DatabaseType::OpenGauss, DatabaseType::Kwdb] {
+ let sql = build_executable_object_source_sql(EditableObjectSourceSqlInput {
+ database_type,
+ object_type: ObjectSourceKind::View,
+ schema: Some("public".to_string()),
+ name: "active users".to_string(),
+ source: " SELECT id, name FROM users WHERE active ".to_string(),
+ })
+ .unwrap();
+ assert_eq!(
+ sql,
+ "CREATE OR REPLACE VIEW \"public\".\"active users\" AS\nSELECT id, name FROM users WHERE active;"
+ );
+ }
}
#[test]
diff --git a/crates/dbx-core/src/sql_dialect.rs b/crates/dbx-core/src/sql_dialect.rs
index bf9fe11acd..b3d2ac0a04 100644
--- a/crates/dbx-core/src/sql_dialect.rs
+++ b/crates/dbx-core/src/sql_dialect.rs
@@ -345,6 +345,7 @@ pub fn is_schema_aware(database_type: DatabaseType) -> bool {
| DatabaseType::Redshift
| DatabaseType::Dameng
| DatabaseType::Gaussdb
+ | DatabaseType::Kwdb
| DatabaseType::Kingbase
| DatabaseType::Highgo
| DatabaseType::Vastbase
@@ -408,6 +409,7 @@ mod tests {
#[test]
fn qualifies_schema_only_for_schema_aware_databases() {
assert_eq!(qualified_table_name(Some(DatabaseType::Postgres), Some("public"), "users"), "\"public\".\"users\"");
+ assert_eq!(qualified_table_name(Some(DatabaseType::Kwdb), Some("public"), "users"), "\"public\".\"users\"");
assert_eq!(qualified_table_name(Some(DatabaseType::Mysql), Some("public"), "users"), "`users`");
assert_eq!(qualified_table_name(Some(DatabaseType::Jdbc), Some("cbsdw_dwd"), "dwd_test_df"), "dwd_test_df");
}
diff --git a/crates/dbx-core/src/table_structure_sql.rs b/crates/dbx-core/src/table_structure_sql.rs
index a6b6069ca0..b730ba3fb3 100644
--- a/crates/dbx-core/src/table_structure_sql.rs
+++ b/crates/dbx-core/src/table_structure_sql.rs
@@ -211,6 +211,7 @@ fn capabilities_for(database_type: Option) -> TableStructureCapabi
Some(
DatabaseType::Postgres
| DatabaseType::Gaussdb
+ | DatabaseType::Kwdb
| DatabaseType::OpenGauss
| DatabaseType::Highgo
| DatabaseType::Vastbase
diff --git a/crates/dbx-core/tests/database_capabilities.rs b/crates/dbx-core/tests/database_capabilities.rs
index d26af273e1..9cec252ac4 100644
--- a/crates/dbx-core/tests/database_capabilities.rs
+++ b/crates/dbx-core/tests/database_capabilities.rs
@@ -85,6 +85,7 @@ fn classifies_agent_database_types() {
assert!(!is_agent_type(&DatabaseType::Mysql));
assert!(!is_agent_type(&DatabaseType::Jdbc));
assert!(!is_agent_type(&DatabaseType::Gaussdb));
+ assert!(!is_agent_type(&DatabaseType::Kwdb));
assert!(!is_agent_type(&DatabaseType::OpenGauss));
}
@@ -102,6 +103,7 @@ fn identifies_single_connection_pool_types() {
assert!(is_single_connection_pool(&DatabaseType::Jdbc));
assert!(!is_single_connection_pool(&DatabaseType::Trino));
assert!(!is_single_connection_pool(&DatabaseType::Postgres));
+ assert!(!is_single_connection_pool(&DatabaseType::Kwdb));
}
#[test]
@@ -110,6 +112,7 @@ fn identifies_metadata_connections_that_drop_database_scope() {
assert!(!is_metadata_connection_scoped(&DatabaseType::Doris));
assert!(!is_metadata_connection_scoped(&DatabaseType::StarRocks));
assert!(!is_metadata_connection_scoped(&DatabaseType::Postgres));
+ assert!(!is_metadata_connection_scoped(&DatabaseType::Kwdb));
assert!(!is_metadata_connection_scoped(&DatabaseType::Oracle));
}
@@ -129,6 +132,7 @@ fn skips_tcp_probe_for_local_file_plugin_and_agent_types() {
assert!(!skips_tcp_probe(&DatabaseType::Postgres));
assert!(!skips_tcp_probe(&DatabaseType::Mysql));
assert!(!skips_tcp_probe(&DatabaseType::Gaussdb));
+ assert!(!skips_tcp_probe(&DatabaseType::Kwdb));
assert!(!skips_tcp_probe(&DatabaseType::OpenGauss));
}
diff --git a/docs/content/docs/databases.cn.mdx b/docs/content/docs/databases.cn.mdx
index eb5f60d321..3668d08070 100644
--- a/docs/content/docs/databases.cn.mdx
+++ b/docs/content/docs/databases.cn.mdx
@@ -13,10 +13,10 @@ DBX 主应用会尽量使用 Rust 原生驱动。部分数据库通过 MySQL 或
| 分组 | 类型 | 说明 |
|---|---|---|
-| 核心 SQL 数据库 | MySQL、PostgreSQL、SQLite、DuckDB、ClickHouse、SQL Server、Oracle、Redshift、DM、GaussDB | 直接出现在 DBX 连接选择器中 |
+| 核心 SQL 数据库 | MySQL、PostgreSQL、SQLite、DuckDB、ClickHouse、SQL Server、Oracle、Redshift、DM、GaussDB、KWDB | 直接出现在 DBX 连接选择器中 |
| 文档与键值数据库 | Redis、MongoDB、Elasticsearch | 根据数据模型使用专门浏览器或查询流程,不完全等同于关系型表格 |
| MySQL 兼容类型 | MariaDB、TiDB、OceanBase、Doris、SelectDB、StarRocks、GoldenDB、自定义 MySQL | 复用 MySQL 风格连接能力 |
-| PostgreSQL 兼容类型 | openGauss、KingBase、HighGo、Vastbase、CockroachDB、自定义 PostgreSQL | 复用 PostgreSQL 风格连接能力 |
+| PostgreSQL 兼容类型 | KWDB、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、虚谷 XuguDB、JDBC | 功能覆盖取决于对应驱动路径 |
@@ -53,6 +53,7 @@ DBX 主应用会尽量使用 Rust 原生驱动。部分数据库通过 MySQL 或
| SunDB | 22000 |
| TDengine | 6041 |
| 虚谷 XuguDB | 5138 |
+| KWDB | 26257 |
SQLite、DuckDB、Access 和 JDBC 在不需要网络端口时,会在连接模型中保存为端口 `0`。
@@ -64,14 +65,14 @@ DBX 只会在具备足够元数据和 SQL 生成能力的数据库上启用高
| 功能 | 支持类型 |
|---|---|
-| 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、虚谷 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 |
-| 字段血缘 | MySQL、PostgreSQL、SQLite、SQL Server、Oracle、Redshift、DM、GaussDB |
-| 数据传输 | MySQL、PostgreSQL、SQLite、SQL Server、Oracle、ClickHouse、DuckDB、DM、GaussDB |
+| Schema 感知树 | PostgreSQL、SQL Server、Oracle、Redshift、DM、GaussDB、KWDB、KingBase、HighGo、Vastbase、JDBC、H2、Snowflake、Trino、DB2、TDengine、虚谷 XuguDB |
+| ER 图 | MySQL、PostgreSQL、SQLite、SQL Server、Oracle、Redshift、DM、GaussDB、KWDB、KingBase、HighGo、Vastbase、GoldenDB、Access、H2、DB2 |
+| 数据库搜索 | MySQL、PostgreSQL、SQLite、SQL Server、Oracle、Redshift、DuckDB、ClickHouse、DM、GaussDB、KWDB、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、KWDB、KingBase、HighGo、Vastbase、GoldenDB、Access |
+| 表结构编辑器 | MySQL、PostgreSQL、SQLite、SQL Server、KWDB |
+| 创建数据库 | MySQL、PostgreSQL、SQL Server、ClickHouse、Oracle、DM、GaussDB、KWDB、Doris、StarRocks、Redshift |
+| 字段血缘 | MySQL、PostgreSQL、SQLite、SQL Server、Oracle、Redshift、DM、GaussDB、KWDB |
+| 数据传输 | MySQL、PostgreSQL、SQLite、SQL Server、Oracle、ClickHouse、DuckDB、DM、GaussDB、KWDB |
| 不支持 SQL 文件执行 | Redis、MongoDB、Elasticsearch |
@@ -82,8 +83,6 @@ DBX 只会在具备足够元数据和 SQL 生成能力的数据库上启用高
大多数网络数据库支持主机、端口、用户名、密码、默认数据库、URL 参数、SSL、连接颜色、SSH 隧道和代理设置。文件型数据库会改为选择本地文件。
-DBX 也可以解析常见连接 URL,例如 MySQL、PostgreSQL、Redis、MongoDB、ClickHouse、SQL Server、Oracle、Elasticsearch、DM、GaussDB、openGauss、TDengine、虚谷 XuguDB 和 Access。
-
### ClickHouse TLS
ClickHouse 连接支持 TLS 加密。创建 ClickHouse 连接时,启用 TLS 开关即可通过 HTTPS 而非 HTTP 进行连接。建议对生产环境的 ClickHouse 实例或 ClickHouse Cloud 部署启用此选项。
diff --git a/docs/content/docs/databases.mdx b/docs/content/docs/databases.mdx
index ff76fa7bd0..5e52f405b3 100644
--- a/docs/content/docs/databases.mdx
+++ b/docs/content/docs/databases.mdx
@@ -13,10 +13,10 @@ The connection dialog exposes ready-to-use profiles with default ports and drive
| Group | Profiles | Notes |
|---|---|---|
-| Core SQL engines | MySQL, PostgreSQL, SQLite, DuckDB, ClickHouse, SQL Server, Oracle, Redshift, DM, GaussDB | Available directly in the DBX connection picker |
+| Core SQL engines | MySQL, PostgreSQL, SQLite, DuckDB, ClickHouse, SQL Server, Oracle, Redshift, DM, GaussDB, KWDB | Available directly in the DBX connection picker |
| Document and key-value engines | Redis, MongoDB, Elasticsearch | Use dedicated browsers or query flows instead of the generic relational grid where appropriate |
| 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 |
+| PostgreSQL-compatible profiles | KWDB, 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, XuguDB, JDBC | Feature coverage depends on the driver path used by that engine |
@@ -53,6 +53,7 @@ The connection dialog exposes ready-to-use profiles with default ports and drive
| SunDB | 22000 |
| TDengine | 6041 |
| XuguDB | 5138 |
+| KWDB | 26257 |
SQLite, DuckDB, Access, and JDBC profiles use port `0` in the saved connection model when a network port is not part of the connection.
@@ -64,14 +65,14 @@ 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, 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, 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 |
-| Field lineage | MySQL, PostgreSQL, SQLite, SQL Server, Oracle, Redshift, DM, GaussDB |
-| Data transfer | MySQL, PostgreSQL, SQLite, SQL Server, Oracle, ClickHouse, DuckDB, DM, GaussDB |
+| Schema-aware tree | PostgreSQL, SQL Server, Oracle, Redshift, DM, GaussDB, KWDB, KingBase, HighGo, Vastbase, JDBC, H2, Snowflake, Trino, DB2, TDengine, XuguDB |
+| ER diagram | MySQL, PostgreSQL, SQLite, SQL Server, Oracle, Redshift, DM, GaussDB, KWDB, KingBase, HighGo, Vastbase, GoldenDB, Access, H2, DB2 |
+| Database search | MySQL, PostgreSQL, SQLite, SQL Server, Oracle, Redshift, DuckDB, ClickHouse, DM, GaussDB, KWDB, 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, KWDB, KingBase, HighGo, Vastbase, GoldenDB, Access |
+| Table structure editor | MySQL, PostgreSQL, SQLite, SQL Server, KWDB |
+| Create database | MySQL, PostgreSQL, SQL Server, ClickHouse, Oracle, DM, GaussDB, KWDB, Doris, StarRocks, Redshift |
+| Field lineage | MySQL, PostgreSQL, SQLite, SQL Server, Oracle, Redshift, DM, GaussDB, KWDB |
+| Data transfer | MySQL, PostgreSQL, SQLite, SQL Server, Oracle, ClickHouse, DuckDB, DM, GaussDB, KWDB |
| SQL file execution unavailable | Redis, MongoDB, Elasticsearch |
@@ -82,7 +83,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, XuguDB, and Access.
+DBX can also parse common connection URLs for engines such as MySQL, PostgreSQL, Redis, MongoDB, ClickHouse, SQL Server, Oracle, Elasticsearch, DM, GaussDB, KWDB, openGauss, TDengine, XuguDB, and Access.
### ClickHouse TLS
diff --git a/packages/app-tests/connectionUrl.test.ts b/packages/app-tests/connectionUrl.test.ts
index 7ac9d7d6d4..56d3d8f442 100644
--- a/packages/app-tests/connectionUrl.test.ts
+++ b/packages/app-tests/connectionUrl.test.ts
@@ -17,6 +17,21 @@ test("parses postgres connection URLs", () => {
});
});
+test("parses KWDB connection URLs", () => {
+ assert.deepEqual(parseConnectionUrl("kwdb://root:secret@kw.example.com/defaultdb?sslmode=require"), {
+ dbType: "kwdb",
+ driverProfile: "kwdb",
+ driverLabel: "KWDB",
+ host: "kw.example.com",
+ port: 26257,
+ username: "root",
+ password: "secret",
+ database: "defaultdb",
+ urlParams: "sslmode=require",
+ ssl: true,
+ });
+});
+
test("parses mysql URLs with encoded credentials", () => {
const parsed = parseConnectionUrl("mysql://root:p%40ss@127.0.0.1/shop?charset=utf8mb4");
diff --git a/packages/app-tests/connectionUrlPlaceholder.test.ts b/packages/app-tests/connectionUrlPlaceholder.test.ts
index 13abcf1213..b7497ac66c 100644
--- a/packages/app-tests/connectionUrlPlaceholder.test.ts
+++ b/packages/app-tests/connectionUrlPlaceholder.test.ts
@@ -8,6 +8,7 @@ const expected: Record = {
starrocks: "mysql://user:password@host:port/database",
postgres: "postgresql://user:password@host:port/database",
gaussdb: "postgresql://user:password@host:port/database",
+ kwdb: "postgresql://user:password@host:port/database",
redshift: "postgresql://user:password@host:port/database",
redis: "redis://:password@host:port/0",
sqlite: "sqlite:///absolute/path/to/database.db",
@@ -45,6 +46,7 @@ test("mysql-family types share the same placeholder", () => {
test("postgres-family types share the same placeholder", () => {
const postgres = connectionUrlPlaceholder("postgres");
assert.equal(connectionUrlPlaceholder("gaussdb"), postgres);
+ assert.equal(connectionUrlPlaceholder("kwdb"), postgres);
assert.equal(connectionUrlPlaceholder("redshift"), postgres);
});
diff --git a/packages/app-tests/databaseCapabilities.test.ts b/packages/app-tests/databaseCapabilities.test.ts
index dc11f39499..f29fba0ef3 100644
--- a/packages/app-tests/databaseCapabilities.test.ts
+++ b/packages/app-tests/databaseCapabilities.test.ts
@@ -183,6 +183,14 @@ test("uses Navicat-style table editing defaults for updateable SQL table engines
requiresTransactionalTableForExistingRows: false,
transaction: true,
});
+ assert.deepEqual(getDatabaseCapability("kwdb").tableData, {
+ insert: true,
+ updateRequiresPrimaryKey: false,
+ deleteRequiresPrimaryKey: false,
+ keylessRowPredicate: true,
+ requiresTransactionalTableForExistingRows: false,
+ transaction: true,
+ });
});
test("keeps conservative table editing defaults for unknown database types", () => {
@@ -210,6 +218,7 @@ test("describes feature support through capability helpers", () => {
assert.equal(supportsTableStructureEditing("oracle"), true);
assert.equal(supportsTableStructureEditing("dameng"), true);
assert.equal(supportsTableStructureEditing("gaussdb"), true);
+ assert.equal(supportsTableStructureEditing("kwdb"), true);
assert.equal(supportsTableStructureEditing("opengauss"), true);
assert.equal(supportsTableStructureEditing("redshift"), true);
assert.equal(supportsTableStructureEditing("clickhouse"), true);
@@ -218,12 +227,15 @@ test("describes feature support through capability helpers", () => {
assert.equal(supportsDatabaseCreation("clickhouse"), true);
assert.equal(supportsDatabaseCreation("sqlite"), false);
assert.equal(supportsFieldLineage("gaussdb"), true);
+ assert.equal(supportsFieldLineage("kwdb"), true);
assert.equal(supportsFieldLineage("trino"), false);
assert.equal(supportsTransfer("duckdb"), true);
assert.equal(supportsTransfer("hive"), false);
assert.equal(supportsDriverManagement("oracle"), true);
assert.equal(supportsDriverManagement("mysql"), false);
+ assert.equal(supportsDriverManagement("kwdb"), false);
assert.equal(usesPostgresLikeStructureCopy("gaussdb"), true);
+ assert.equal(usesPostgresLikeStructureCopy("kwdb"), true);
assert.equal(usesPostgresLikeStructureCopy("mysql"), false);
assert.equal(supportsObjectBrowser("mysql"), true);
assert.equal(supportsObjectBrowser("mongodb"), false);
diff --git a/packages/app-tests/tableStructureCapabilities.test.ts b/packages/app-tests/tableStructureCapabilities.test.ts
index dbe174ab04..e12c991858 100644
--- a/packages/app-tests/tableStructureCapabilities.test.ts
+++ b/packages/app-tests/tableStructureCapabilities.test.ts
@@ -14,7 +14,7 @@ test("sqlite-family and duckdb do not support table comments", () => {
});
test("postgres-like databases expose safe structure editing capabilities", () => {
- for (const dbType of ["postgres", "gaussdb", "opengauss", "highgo", "vastbase", "kingbase"] as const) {
+ for (const dbType of ["postgres", "gaussdb", "kwdb", "opengauss", "highgo", "vastbase", "kingbase"] as const) {
const caps = getTableStructureCapabilities(dbType);
assert.equal(caps.dialect, "postgres", `${dbType} should reuse postgres DDL`);
assert.equal(caps.createTable, true, `${dbType} should create tables`);
diff --git a/packages/mcp-server/src/index.ts b/packages/mcp-server/src/index.ts
index 945516f6b1..bb0d2de349 100644
--- a/packages/mcp-server/src/index.ts
+++ b/packages/mcp-server/src/index.ts
@@ -46,7 +46,7 @@ function formatQueryToolResult(result: QueryResult, title?: string) {
}
export const DBX_CONNECTION_TYPE_DESCRIPTION =
- "Database type: postgres, mysql, sqlite, rqlite, 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, iris, neo4j, cassandra, bigquery, kylin, sundb, tdengine, xugu, jdbc, access";
+ "Database type: postgres, mysql, sqlite, rqlite, redis, duckdb, clickhouse, sqlserver, mongodb, oracle, elasticsearch, doris, starrocks, redshift, dameng, kingbase, highgo, vastbase, goldendb, gaussdb, kwdb, yashandb, databricks, saphana, teradata, vertica, firebird, exasol, opengauss, oceanbase-oracle, gbase, h2, snowflake, trino, hive, db2, informix, iris, 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;
@@ -181,7 +181,7 @@ export function createDbxMcpServer(backend: Backend, options: { isWebMode?: bool
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 = { rqlite: 4001, tdengine: 6041, xugu: 5138 };
+ const DEFAULT_PORTS: Record = { kwdb: 26257, rqlite: 4001, 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({
diff --git a/packages/node-core/src/diagnostics.ts b/packages/node-core/src/diagnostics.ts
index eb6e7cd5c5..70871f4e6a 100644
--- a/packages/node-core/src/diagnostics.ts
+++ b/packages/node-core/src/diagnostics.ts
@@ -11,6 +11,7 @@ export const DIRECT_QUERY_TYPES = [
"sqlite",
"rqlite",
"gaussdb",
+ "kwdb",
"opengauss",
] as const;
diff --git a/src-tauri/src/commands/connection.rs b/src-tauri/src/commands/connection.rs
index 7d80ba7779..b0bc008870 100644
--- a/src-tauri/src/commands/connection.rs
+++ b/src-tauri/src/commands/connection.rs
@@ -290,15 +290,17 @@ pub async fn test_connection(state: State<'_, Arc>, config: Connection
Err(e) => Err(e),
}
}
- DatabaseType::Postgres | DatabaseType::Redshift | DatabaseType::Gaussdb | DatabaseType::OpenGauss => {
- match db::postgres::connect(&url, connect_timeout).await {
- Ok(pool) => {
- pool.close();
- Ok("Connection successful".to_string())
- }
- Err(e) => Err(e),
+ DatabaseType::Postgres
+ | DatabaseType::Redshift
+ | DatabaseType::Gaussdb
+ | DatabaseType::Kwdb
+ | DatabaseType::OpenGauss => match db::postgres::connect(&url, connect_timeout).await {
+ Ok(pool) => {
+ pool.close();
+ Ok("Connection successful".to_string())
}
- }
+ Err(e) => Err(e),
+ },
DatabaseType::Sqlite => {
let extensions = db::sqlite::sqlite_extension_specs_from_url_params(config.url_params.as_deref())
.into_iter()
@@ -457,9 +459,11 @@ pub async fn connect_db(state: State<'_, Arc>, config: ConnectionConfi
connect_bare_metadata_pool(&db_config, &host, port, connect_timeout).await?,
MysqlMode::Bare,
),
- DatabaseType::Postgres | DatabaseType::Redshift | DatabaseType::Gaussdb | DatabaseType::OpenGauss => {
- PoolKind::Postgres(db::postgres::connect(&url, connect_timeout).await?)
- }
+ DatabaseType::Postgres
+ | DatabaseType::Redshift
+ | DatabaseType::Gaussdb
+ | DatabaseType::Kwdb
+ | DatabaseType::OpenGauss => PoolKind::Postgres(db::postgres::connect(&url, connect_timeout).await?),
DatabaseType::Sqlite => {
let extensions = db::sqlite::sqlite_extension_specs_from_url_params(db_config.url_params.as_deref())
.into_iter()