From 799d20a2cf35d4a792889e6fbbef3bc2dff434c3 Mon Sep 17 00:00:00 2001 From: Vergil Lai Date: Sat, 13 Jun 2026 20:23:36 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20Manticore=20Search=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=20(#1145)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 添加 Manticore Search 支持 * 更新 Manticore Search 文档 --- README.md | 4 +- README.zh-CN.md | 4 +- .../public/icons/database/manticoresearch.png | Bin 0 -> 10184 bytes .../connection/ConnectionDialog.vue | 18 ++++- .../src/components/icons/DatabaseIcon.vue | 1 + .../desktop/src/lib/connectionPresentation.ts | 3 +- .../desktop/src/lib/databaseFeatureSupport.ts | 4 +- apps/desktop/src/lib/visibleDatabases.ts | 3 +- apps/desktop/src/stores/connectionStore.ts | 3 +- apps/desktop/src/types/database.ts | 3 +- .../assets/database-drivers.manifest.json | 29 ++++++++ crates/dbx-core/src/connection.rs | 2 +- crates/dbx-core/src/db/mysql.rs | 8 ++- crates/dbx-core/src/models/connection.rs | 26 ++++--- crates/dbx-core/src/schema.rs | 65 ++++++++++++++++-- .../dbx-core/src/schema/providers/native.rs | 28 +++++++- crates/dbx-core/src/sql.rs | 34 ++++++--- crates/dbx-core/src/sql_analysis.rs | 2 +- .../dbx-core/src/sql_dialect/identifiers.rs | 1 + crates/dbx-core/src/sql_risk.rs | 2 +- docs/app/[lang]/databases/page.tsx | 1 + docs/app/[lang]/page.tsx | 1 + docs/content/docs/databases.cn.mdx | 3 +- docs/content/docs/databases.mdx | 3 +- .../public/icons/database/manticoresearch.png | Bin 0 -> 10184 bytes .../connectionUrlPlaceholder.test.ts | 2 + .../app-tests/databaseCapabilities.test.ts | 28 ++++++++ .../tableStructureCapabilities.test.ts | 2 +- packages/app-tests/visibleDatabases.test.ts | 2 + packages/mcp-server/src/index.ts | 2 +- packages/node-core/src/database.ts | 2 +- packages/node-core/src/diagnostics.ts | 2 +- .../node-core/tests/driver-manifest.test.ts | 15 ++++ src-tauri/src/commands/connection.rs | 4 +- 34 files changed, 258 insertions(+), 49 deletions(-) create mode 100644 apps/desktop/public/icons/database/manticoresearch.png create mode 100644 docs/public/icons/database/manticoresearch.png diff --git a/README.md b/README.md index b0306c139..9a6271adb 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ ### 50+ Databases, One Tool -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. New native and agent-driven drivers also cover Databricks, SAP HANA, Teradata, Vertica, Firebird, Exasol, YashanDB, GBase, Databend, RQLite, Turso, InfluxDB, IoTDB, etcd, IRIS, and more. 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, Manticore Search, 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. New native and agent-driven drivers also cover Databricks, SAP HANA, Teradata, Vertica, Firebird, Exasol, YashanDB, GBase, Databend, RQLite, Turso, InfluxDB, IoTDB, etcd, IRIS, and more. All in a single ~15 MB app. No bundled Chromium. ### Query Editor @@ -324,7 +324,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, KWDB, KingBase, Vastbase, GoldenDB, Doris, SelectDB, StarRocks, Redshift, DM, TDengine, XuguDB, CockroachDB, Access, HighGo, and more. Agent/JDBC-oriented profiles extend support to H2, Snowflake, Trino, Hive, DB2, Informix, Neo4j, Cassandra, BigQuery, Kylin, SunDB, Databricks, SAP HANA, Teradata, Vertica, Firebird, Exasol, YashanDB, GBase, Databend, RQLite, Turso, InfluxDB, IoTDB, etcd, IRIS, 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, Manticore Search, Redshift, DM, TDengine, XuguDB, CockroachDB, Access, HighGo, and more. Agent/JDBC-oriented profiles extend support to H2, Snowflake, Trino, Hive, DB2, Informix, Neo4j, Cassandra, BigQuery, Kylin, SunDB, Databricks, SAP HANA, Teradata, Vertica, Firebird, Exasol, YashanDB, GBase, Databend, RQLite, Turso, InfluxDB, IoTDB, etcd, IRIS, and custom JDBC connections.
diff --git a/README.zh-CN.md b/README.zh-CN.md index b8f78e512..3610cae54 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -87,7 +87,7 @@ ### 50+ 种数据库,一个工具搞定 -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。新增的原生与 Agent 驱动还覆盖了 Databricks、SAP HANA、Teradata、Vertica、Firebird、Exasol、崖山 YashanDB、GBase、Databend、RQLite、Turso、InfluxDB、IoTDB、etcd、IRIS 等。全部装进约 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、Manticore Search、Redshift、DM、TDengine、虚谷 XuguDB、CockroachDB、Access、HighGo 等数据库都能直接连接。Agent/JDBC 方向的配置还可扩展到 H2、Snowflake、Trino、Hive、DB2、Informix、Neo4j、Cassandra、BigQuery、Kylin、SunDB 和自定义 JDBC。新增的原生与 Agent 驱动还覆盖了 Databricks、SAP HANA、Teradata、Vertica、Firebird、Exasol、崖山 YashanDB、GBase、Databend、RQLite、Turso、InfluxDB、IoTDB、etcd、IRIS 等。全部装进约 15 MB 的应用里,不内嵌 Chromium。 ### 查询编辑器 @@ -320,7 +320,7 @@ DBX 仅 15 MB,无需运行时依赖(无需 Java、无需 Python)。AI 和
支持哪些数据库? -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、Databricks、SAP HANA、Teradata、Vertica、Firebird、Exasol、崖山 YashanDB、GBase、Databend、RQLite、Turso、InfluxDB、IoTDB、etcd、IRIS 及自定义 JDBC 连接。 +MySQL、PostgreSQL、SQLite、Redis、MongoDB、DuckDB、ClickHouse、SQL Server、Oracle、Elasticsearch、MariaDB、TiDB、OceanBase、openGauss、GaussDB、KWDB、KingBase、Vastbase、GoldenDB、Doris、SelectDB、StarRocks、Manticore Search、Redshift、DM、TDengine、虚谷 XuguDB、CockroachDB、Access、HighGo 等。JDBC 方向配置可扩展到 H2、Snowflake、Trino、Hive、DB2、Informix、Neo4j、Cassandra、BigQuery、Kylin、SunDB、Databricks、SAP HANA、Teradata、Vertica、Firebird、Exasol、崖山 YashanDB、GBase、Databend、RQLite、Turso、InfluxDB、IoTDB、etcd、IRIS 及自定义 JDBC 连接。
diff --git a/apps/desktop/public/icons/database/manticoresearch.png b/apps/desktop/public/icons/database/manticoresearch.png new file mode 100644 index 0000000000000000000000000000000000000000..420cb72a596fc8d3a7358c64eed5fdaa3f2efc3b GIT binary patch literal 10184 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 literal 0 HcmV?d00001 diff --git a/apps/desktop/src/components/connection/ConnectionDialog.vue b/apps/desktop/src/components/connection/ConnectionDialog.vue index e7af39260..cad99a3de 100644 --- a/apps/desktop/src/components/connection/ConnectionDialog.vue +++ b/apps/desktop/src/components/connection/ConnectionDialog.vue @@ -420,6 +420,14 @@ const driverProfiles: Record< icon: "starrocks", urlParams: "", }, + manticoresearch: { + type: "manticoresearch", + port: 9306, + user: "root", + label: "Manticore Search", + icon: "manticoresearch", + urlParams: "", + }, redshift: { type: "redshift", port: 5439, user: "awsuser", label: "Redshift", icon: "redshift" }, cockroachdb: { type: "postgres", @@ -734,6 +742,7 @@ const iconTypeMap: Record = { doris: "doris", selectdb: "selectdb", starrocks: "starrocks", + manticoresearch: "manticoresearch", redshift: "redshift", cockroachdb: "cockroachdb", tdengine: "tdengine", @@ -819,6 +828,9 @@ const dbOptions: DbOption[] = [ { value: "influxdb", label: "InfluxDB" }, { value: "iris", label: "IRIS" }, { value: "jdbc", label: "JDBC" }, + { value: "manticoresearch", label: "Manticore Search" }, + { value: "custom_mysql", label: "Custom (MySQL)" }, + { value: "custom_postgres", label: "Custom (PostgreSQL)" }, ]; const dbCategories = computed(() => [{ key: "all", title: "", options: dbOptions }]); @@ -867,6 +879,7 @@ const sqliteExtensionPaths = computed({ const tlsCapableDatabaseTypes = new Set(["mysql", "postgres", "redshift", "gaussdb", "kwdb", "opengauss", "redis", "etcd", "clickhouse", "elasticsearch", "influxdb"]); const supportsTlsToggle = computed(() => tlsCapableDatabaseTypes.has(form.value.db_type)); const supportsCaCertificatePath = computed(() => form.value.db_type === "clickhouse"); +const supportsGenericUrlParams = computed(() => form.value.db_type !== "manticoresearch"); const bareMysqlProfiles = new Set(["doris", "starrocks", "selectdb", "oceanbase"]); const supportsMysqlTlsOptions = computed(() => form.value.db_type === "mysql" && !bareMysqlProfiles.has(selectedType.value)); const mysqlTlsMode = computed({ @@ -1072,6 +1085,9 @@ function connectionConfigForSubmit(id: string): ConnectionConfig { config.query_timeout_secs = Number.isFinite(queryTimeout) && queryTimeout >= 0 ? queryTimeout : 30; const idleTimeout = Number(config.idle_timeout_secs); config.idle_timeout_secs = Number.isFinite(idleTimeout) && idleTimeout >= 0 ? idleTimeout : 60; + if (config.db_type === "manticoresearch") { + config.url_params = ""; + } if (!config.one_time) config.one_time = undefined; if (!config.read_only) config.read_only = undefined; if (config.db_type === "mongodb" && !mongoUseUrl.value) { @@ -2606,7 +2622,7 @@ function openExternalUrl(url: string) { -
+
= { bigquery: "bigquery", cassandra: "cassandra", doris: "doris", + manticoresearch: "manticoresearch.png", selectdb: "selectdb", tdengine: "tdengine", starrocks: "starrocks", diff --git a/apps/desktop/src/lib/connectionPresentation.ts b/apps/desktop/src/lib/connectionPresentation.ts index 6bb3e0149..44aa463c0 100644 --- a/apps/desktop/src/lib/connectionPresentation.ts +++ b/apps/desktop/src/lib/connectionPresentation.ts @@ -82,6 +82,7 @@ export function connectionUrlPlaceholder(dbType: DatabaseType): string { case "mysql": case "doris": case "starrocks": + case "manticoresearch": return "mysql://user:password@host:port/database"; case "postgres": @@ -162,4 +163,4 @@ export function connectionOptionSubtitle(connection?: ConnectionPresentationConf export function connectionRedactedOptionSubtitle(connection?: ConnectionPresentationConfig): string { return [connectionDriverLabel(connection), connectionRedactedEndpointLabel(connection)].filter(Boolean).join(" · "); -} +} \ No newline at end of file diff --git a/apps/desktop/src/lib/databaseFeatureSupport.ts b/apps/desktop/src/lib/databaseFeatureSupport.ts index fef170e2a..928cbaeb3 100644 --- a/apps/desktop/src/lib/databaseFeatureSupport.ts +++ b/apps/desktop/src/lib/databaseFeatureSupport.ts @@ -82,9 +82,9 @@ export function supportsObjectBrowserTreeNode(dbType: DatabaseType | undefined, } export function supportsTableTruncate(dbType?: DatabaseType): boolean { - return !!dbType && dbType !== "sqlite" && dbType !== "rqlite" && dbType !== "turso" && dbType !== "duckdb" && dbType !== "influxdb"; + return !!dbType && dbType !== "sqlite" && dbType !== "rqlite" && dbType !== "turso" && dbType !== "duckdb" && dbType !== "influxdb" && dbType !== "manticoresearch"; } export function usesPostgresLikeStructureCopy(dbType?: DatabaseType): boolean { return !!dbType && PG_LIKE_STRUCTURE_TYPES.has(dbType); -} +} \ No newline at end of file diff --git a/apps/desktop/src/lib/visibleDatabases.ts b/apps/desktop/src/lib/visibleDatabases.ts index 1c616b596..fd7eac3d3 100644 --- a/apps/desktop/src/lib/visibleDatabases.ts +++ b/apps/desktop/src/lib/visibleDatabases.ts @@ -4,6 +4,7 @@ const SYSTEM_DATABASE_RULES: Partial>> mysql: new Set(["information_schema", "mysql", "performance_schema", "sys"]), doris: new Set(["information_schema", "mysql", "performance_schema", "sys"]), starrocks: new Set(["information_schema", "mysql", "performance_schema", "sys"]), + manticoresearch: new Set(["information_schema", "mysql", "performance_schema", "sys"]), goldendb: new Set(["information_schema", "mysql", "performance_schema", "sys"]), gbase: new Set(["information_schema", "mysql", "performance_schema", "sys"]), postgres: new Set(["template0", "template1"]), @@ -94,4 +95,4 @@ export function filterDatabaseNamesForConnection(databaseNames: string[], connec return databaseNames; } return databaseNames.filter((name) => !isSystemDatabaseName(connection?.db_type, name)); -} +} \ No newline at end of file diff --git a/apps/desktop/src/stores/connectionStore.ts b/apps/desktop/src/stores/connectionStore.ts index ed9e27699..b5f41f4eb 100644 --- a/apps/desktop/src/stores/connectionStore.ts +++ b/apps/desktop/src/stores/connectionStore.ts @@ -241,6 +241,7 @@ export const useConnectionStore = defineStore("connection", () => { elasticsearch: "Elasticsearch", doris: "Doris", starrocks: "StarRocks", + manticoresearch: "Manticore Search", redshift: "Redshift", dameng: "DM (Dameng)", gaussdb: "GaussDB", @@ -2768,4 +2769,4 @@ export const useConnectionStore = defineStore("connection", () => { if (changed) updateLayoutAndRebuild(layout); }, }; -}); +}); \ No newline at end of file diff --git a/apps/desktop/src/types/database.ts b/apps/desktop/src/types/database.ts index e96836f06..568229969 100644 --- a/apps/desktop/src/types/database.ts +++ b/apps/desktop/src/types/database.ts @@ -13,6 +13,7 @@ export type DatabaseType = | "elasticsearch" | "doris" | "starrocks" + | "manticoresearch" | "databend" | "redshift" | "dameng" @@ -542,4 +543,4 @@ export interface SavedSqlFile { export interface SavedSqlLibrary { folders: SavedSqlFolder[]; files: SavedSqlFile[]; -} +} \ No newline at end of file diff --git a/crates/dbx-core/assets/database-drivers.manifest.json b/crates/dbx-core/assets/database-drivers.manifest.json index a1cd9b1c2..17ef47063 100644 --- a/crates/dbx-core/assets/database-drivers.manifest.json +++ b/crates/dbx-core/assets/database-drivers.manifest.json @@ -378,6 +378,35 @@ "driverManagement": false } }, + { + "dbType": "manticoresearch", + "label": "Manticore Search", + "runtimeMode": "native", + "mcpMode": "direct", + "singleConnectionPool": false, + "metadataConnectionScoped": false, + "skipTcpProbe": false, + "defaultPort": 9306, + "supportLevel": "browse", + "capabilities": { + "queryExecution": true, + "metadataBrowse": true, + "objectBrowser": false, + "objectSource": false, + "schemaSearch": false, + "diagram": false, + "tableDataEdit": false, + "tableStructureEdit": false, + "tableImport": false, + "dataTransfer": false, + "sqlFileExecution": true, + "databaseCreate": false, + "fieldLineage": false, + "sqlExplain": false, + "userAdmin": false, + "driverManagement": false + } + }, { "dbType": "redshift", "label": "Redshift", diff --git a/crates/dbx-core/src/connection.rs b/crates/dbx-core/src/connection.rs index e4ac498e2..218479f2e 100644 --- a/crates/dbx-core/src/connection.rs +++ b/crates/dbx-core/src/connection.rs @@ -358,7 +358,7 @@ impl AppState { .await?; PoolKind::Mysql(pool, mode) } - DatabaseType::Doris | DatabaseType::StarRocks | DatabaseType::Databend => { + DatabaseType::Doris | DatabaseType::StarRocks | DatabaseType::ManticoreSearch | DatabaseType::Databend => { let pool = if database.is_none() { connect_bare_metadata_pool(&db_config, &host, port, connect_timeout, mysql_pool_max_connections) .await? diff --git a/crates/dbx-core/src/db/mysql.rs b/crates/dbx-core/src/db/mysql.rs index 60cc0ca4c..bdc5b2fb1 100644 --- a/crates/dbx-core/src/db/mysql.rs +++ b/crates/dbx-core/src/db/mysql.rs @@ -29,8 +29,12 @@ impl MySqlQueryDialect { pub fn for_connection(db_type: DatabaseType, driver_profile: Option<&str>) -> Self { let profile = driver_profile.map(str::to_ascii_lowercase); Self { - supports_admin_show_results: matches!(db_type, DatabaseType::Doris | DatabaseType::StarRocks) - || profile.as_deref().is_some_and(|profile| matches!(profile, "doris" | "selectdb" | "starrocks")), + supports_admin_show_results: matches!( + db_type, + DatabaseType::Doris | DatabaseType::StarRocks | DatabaseType::ManticoreSearch + ) || profile + .as_deref() + .is_some_and(|profile| matches!(profile, "doris" | "selectdb" | "starrocks" | "manticoresearch")), } } } diff --git a/crates/dbx-core/src/models/connection.rs b/crates/dbx-core/src/models/connection.rs index 6cf07e66c..23997f38a 100644 --- a/crates/dbx-core/src/models/connection.rs +++ b/crates/dbx-core/src/models/connection.rs @@ -240,6 +240,8 @@ pub enum DatabaseType { Doris, #[serde(rename = "starrocks")] StarRocks, + #[serde(rename = "manticoresearch")] + ManticoreSearch, Databend, Redshift, Dameng, @@ -593,12 +595,10 @@ impl ConnectionConfig { } pub fn needs_bare_mysql(&self) -> bool { - matches!(self.db_type, DatabaseType::Doris | DatabaseType::StarRocks) - || self - .driver_profile - .as_deref() - .map(|p| p.to_lowercase()) - .is_some_and(|p| matches!(p.as_str(), "doris" | "starrocks" | "selectdb" | "oceanbase")) + matches!(self.db_type, DatabaseType::Doris | DatabaseType::StarRocks | DatabaseType::ManticoreSearch) + || self.driver_profile.as_deref().map(|p| p.to_lowercase()).is_some_and(|p| { + matches!(p.as_str(), "doris" | "starrocks" | "manticoresearch" | "selectdb" | "oceanbase") + }) } pub fn canonicalized(&self) -> Self { @@ -656,7 +656,11 @@ impl ConnectionConfig { let fragment = self.redis_tls_insecure_fragment(); format!("{scheme}://{host}:{port}/{fragment}") } - DatabaseType::Mysql | DatabaseType::Doris | DatabaseType::StarRocks | DatabaseType::Databend => { + DatabaseType::Mysql + | DatabaseType::Doris + | DatabaseType::StarRocks + | DatabaseType::ManticoreSearch + | DatabaseType::Databend => { let suffix = if params.is_empty() { String::new() } else { format!("?{params}") }; format!("mysql://{host}:{port}{db_part}{suffix}") } @@ -777,7 +781,11 @@ impl ConnectionConfig { format!("{scheme}://{username}:{password}@{host}:{port}/{fragment}") } } - DatabaseType::Mysql | DatabaseType::Doris | DatabaseType::StarRocks | DatabaseType::Databend => { + DatabaseType::Mysql + | DatabaseType::Doris + | DatabaseType::StarRocks + | DatabaseType::ManticoreSearch + | DatabaseType::Databend => { let suffix = if params.is_empty() { String::new() } else { format!("?{params}") }; format!("mysql://{}:{}@{host}:{port}{db_part}{suffix}", username, password) } @@ -958,7 +966,7 @@ impl ConnectionConfig { DatabaseType::Mysql => { normalize_mysql_url_params(value, self.mysql_uses_tls(), self.ca_cert_path.trim().is_empty()) } - DatabaseType::Doris | DatabaseType::StarRocks | DatabaseType::Databend => { + DatabaseType::Doris | DatabaseType::StarRocks | DatabaseType::ManticoreSearch | DatabaseType::Databend => { normalize_bare_mysql_url_params(value) } DatabaseType::Postgres | DatabaseType::Redshift => normalize_postgres_url_params(value, self.ssl), diff --git a/crates/dbx-core/src/schema.rs b/crates/dbx-core/src/schema.rs index c3ccf6744..ca6b99e25 100644 --- a/crates/dbx-core/src/schema.rs +++ b/crates/dbx-core/src/schema.rs @@ -319,7 +319,9 @@ async fn list_databases_once(state: &AppState, connection_id: &str) -> Result { - db::mysql::list_databases_show(p).await + db::mysql::list_databases_show(p) + .await + .map(|databases| filter_mysql_system_databases_for_config(databases, db_config.as_ref())) } PoolKind::Mysql(p, mode) => dispatch_mysql!(p, mode, db::mysql::list_databases, db::ob_oracle::list_databases), PoolKind::Postgres(p) => db::postgres::list_databases(p).await, @@ -599,8 +601,8 @@ fn filter_table_infos( #[cfg(test)] mod tests { use super::{ - clickhouse_metadata_database, deduplicate_column_infos, filter_table_infos, - is_agent_postgres_metadata_fallback_config, + clickhouse_metadata_database, deduplicate_column_infos, filter_mysql_system_databases_for_config, + filter_table_infos, is_agent_postgres_metadata_fallback_config, }; #[cfg(feature = "duckdb-bundled")] use super::{duckdb_attach_database, duckdb_list_databases, duckdb_query_tables_in_database}; @@ -675,6 +677,39 @@ mod tests { } } + fn test_database_info(name: &str) -> super::db::DatabaseInfo { + super::db::DatabaseInfo { name: name.to_string() } + } + + #[test] + fn manticoresearch_database_list_filters_mysql_system_databases() { + let databases = vec![ + test_database_info("Manticore"), + test_database_info("information_schema"), + test_database_info("mysql"), + test_database_info("performance_schema"), + test_database_info("sys"), + ]; + let config = test_connection_config(DatabaseType::ManticoreSearch); + + let filtered = filter_mysql_system_databases_for_config(databases, Some(&config)); + + assert_eq!(filtered.into_iter().map(|database| database.name).collect::>(), vec!["Manticore"]); + } + + #[test] + fn doris_database_list_keeps_system_databases() { + let databases = vec![test_database_info("information_schema"), test_database_info("analytics")]; + let config = test_connection_config(DatabaseType::Doris); + + let filtered = filter_mysql_system_databases_for_config(databases, Some(&config)); + + assert_eq!( + filtered.into_iter().map(|database| database.name).collect::>(), + vec!["information_schema", "analytics"] + ); + } + #[test] fn filter_table_infos_applies_filter_offset_and_limit() { let tables = vec![ @@ -1469,8 +1504,28 @@ fn is_opengauss_family_config(config: &ConnectionConfig) -> bool { } fn is_doris_family_config(config: &ConnectionConfig) -> bool { - matches!(config.db_type, DatabaseType::Doris | DatabaseType::StarRocks) - || matches!(config.driver_profile.as_deref(), Some("doris" | "selectdb" | "starrocks")) + matches!(config.db_type, DatabaseType::Doris | DatabaseType::StarRocks | DatabaseType::ManticoreSearch) + || matches!(config.driver_profile.as_deref(), Some("doris" | "selectdb" | "starrocks" | "manticoresearch")) +} + +fn is_manticoresearch_config(config: &ConnectionConfig) -> bool { + matches!(config.db_type, DatabaseType::ManticoreSearch) + || matches!(config.driver_profile.as_deref(), Some("manticoresearch")) +} + +fn filter_mysql_system_databases_for_config( + databases: Vec, + config: Option<&ConnectionConfig>, +) -> Vec { + if !config.is_some_and(is_manticoresearch_config) { + return databases; + } + + databases.into_iter().filter(|database| !is_mysql_system_database(&database.name)).collect() +} + +fn is_mysql_system_database(name: &str) -> bool { + matches!(name.to_ascii_lowercase().as_str(), "information_schema" | "mysql" | "performance_schema" | "sys") } fn sql_string(value: &str) -> String { diff --git a/crates/dbx-core/src/schema/providers/native.rs b/crates/dbx-core/src/schema/providers/native.rs index 013e3a6fa..1d62799d7 100644 --- a/crates/dbx-core/src/schema/providers/native.rs +++ b/crates/dbx-core/src/schema/providers/native.rs @@ -7,7 +7,9 @@ pub(in crate::schema) async fn list_databases( config: Option<&ConnectionConfig>, ) -> Result, String> { match pool { - PoolKind::Mysql(p, _) if config.is_some_and(is_doris_family_config) => db::mysql::list_databases_show(p).await, + PoolKind::Mysql(p, _) if config.is_some_and(is_doris_family_config) => db::mysql::list_databases_show(p) + .await + .map(|databases| filter_mysql_system_databases_for_config(databases, config)), PoolKind::Mysql(p, mode) if *mode == MysqlMode::OceanBaseOracle => db::ob_oracle::list_databases(p).await, PoolKind::Mysql(p, _) => db::mysql::list_databases(p).await, PoolKind::Postgres(p) => db::postgres::list_databases(p).await, @@ -262,6 +264,26 @@ fn is_opengauss_family_config(config: &ConnectionConfig) -> bool { } fn is_doris_family_config(config: &ConnectionConfig) -> bool { - matches!(config.db_type, DatabaseType::Doris | DatabaseType::StarRocks) - || matches!(config.driver_profile.as_deref(), Some("doris" | "selectdb" | "starrocks")) + matches!(config.db_type, DatabaseType::Doris | DatabaseType::StarRocks | DatabaseType::ManticoreSearch) + || matches!(config.driver_profile.as_deref(), Some("doris" | "selectdb" | "starrocks" | "manticoresearch")) +} + +fn is_manticoresearch_config(config: &ConnectionConfig) -> bool { + matches!(config.db_type, DatabaseType::ManticoreSearch) + || matches!(config.driver_profile.as_deref(), Some("manticoresearch")) +} + +fn filter_mysql_system_databases_for_config( + databases: Vec, + config: Option<&ConnectionConfig>, +) -> Vec { + if !config.is_some_and(is_manticoresearch_config) { + return databases; + } + + databases.into_iter().filter(|database| !is_mysql_system_database(&database.name)).collect() +} + +fn is_mysql_system_database(name: &str) -> bool { + matches!(name.to_ascii_lowercase().as_str(), "information_schema" | "mysql" | "performance_schema" | "sys") } diff --git a/crates/dbx-core/src/sql.rs b/crates/dbx-core/src/sql.rs index 4d5dcfcea..62753ac9e 100644 --- a/crates/dbx-core/src/sql.rs +++ b/crates/dbx-core/src/sql.rs @@ -64,7 +64,11 @@ impl SqlParsingOptions { Self { supports_hash_line_comments: matches!( db_type, - DatabaseType::Mysql | DatabaseType::Doris | DatabaseType::StarRocks | DatabaseType::Goldendb + DatabaseType::Mysql + | DatabaseType::Doris + | DatabaseType::StarRocks + | DatabaseType::ManticoreSearch + | DatabaseType::Goldendb ), supports_oracle_plsql_blocks: matches!( db_type, @@ -1203,13 +1207,27 @@ fn executable_sql_keyword_matches(token: &str, keyword: &str) -> bool { } fn is_mysql_compatible_import_target(db_type: &DatabaseType, driver_profile: Option<&str>) -> bool { - matches!(db_type, DatabaseType::Mysql | DatabaseType::Doris | DatabaseType::StarRocks | DatabaseType::Goldendb) - || driver_profile.map(|profile| profile.to_ascii_lowercase()).is_some_and(|profile| { - matches!( - profile.as_str(), - "mariadb" | "tidb" | "oceanbase" | "custom_mysql" | "doris" | "starrocks" | "selectdb" | "goldendb" - ) - }) + matches!( + db_type, + DatabaseType::Mysql + | DatabaseType::Doris + | DatabaseType::StarRocks + | DatabaseType::ManticoreSearch + | DatabaseType::Goldendb + ) || driver_profile.map(|profile| profile.to_ascii_lowercase()).is_some_and(|profile| { + matches!( + profile.as_str(), + "mariadb" + | "tidb" + | "oceanbase" + | "custom_mysql" + | "doris" + | "starrocks" + | "manticoresearch" + | "selectdb" + | "goldendb" + ) + }) } fn mysql_executable_comment_body(statement: &str) -> Option<&str> { diff --git a/crates/dbx-core/src/sql_analysis.rs b/crates/dbx-core/src/sql_analysis.rs index 642b04fcb..15e35b030 100644 --- a/crates/dbx-core/src/sql_analysis.rs +++ b/crates/dbx-core/src/sql_analysis.rs @@ -91,7 +91,7 @@ fn starts_with_duckdb_parser_gap_sql(sql: &str) -> bool { fn normalize_dialect(dialect: Option<&str>) -> String { match dialect.unwrap_or("generic").to_ascii_lowercase().as_str() { "postgres" | "postgresql" | "redshift" | "opengauss" | "gaussdb" | "highgo" => "postgres".to_string(), - "mysql" | "mariadb" | "doris" | "starrocks" | "oceanbase" => "mysql".to_string(), + "mysql" | "mariadb" | "doris" | "starrocks" | "manticoresearch" | "oceanbase" => "mysql".to_string(), "sqlite" => "sqlite".to_string(), "sqlserver" | "mssql" => "sqlserver".to_string(), "clickhouse" => "clickhouse".to_string(), diff --git a/crates/dbx-core/src/sql_dialect/identifiers.rs b/crates/dbx-core/src/sql_dialect/identifiers.rs index 5d4a11971..f1ffb61fe 100644 --- a/crates/dbx-core/src/sql_dialect/identifiers.rs +++ b/crates/dbx-core/src/sql_dialect/identifiers.rs @@ -36,6 +36,7 @@ pub fn quote_table_identifier(database_type: Option, name: &str) - DatabaseType::Mysql | DatabaseType::Goldendb | DatabaseType::StarRocks + | DatabaseType::ManticoreSearch | DatabaseType::Hive | DatabaseType::Databend | DatabaseType::Tdengine diff --git a/crates/dbx-core/src/sql_risk.rs b/crates/dbx-core/src/sql_risk.rs index c4339374f..9b330b010 100644 --- a/crates/dbx-core/src/sql_risk.rs +++ b/crates/dbx-core/src/sql_risk.rs @@ -34,7 +34,7 @@ impl std::fmt::Display for SqlRisk { fn normalize_dialect(dialect: &str) -> &'static str { match dialect.to_ascii_lowercase().as_str() { "postgres" | "postgresql" | "redshift" | "opengauss" | "gaussdb" | "highgo" => "postgres", - "mysql" | "mariadb" | "doris" | "starrocks" | "oceanbase" => "mysql", + "mysql" | "mariadb" | "doris" | "starrocks" | "manticoresearch" | "oceanbase" => "mysql", "sqlite" => "sqlite", "sqlserver" | "mssql" => "sqlserver", "clickhouse" => "clickhouse", diff --git a/docs/app/[lang]/databases/page.tsx b/docs/app/[lang]/databases/page.tsx index b1f18cbf5..fc3d4900a 100644 --- a/docs/app/[lang]/databases/page.tsx +++ b/docs/app/[lang]/databases/page.tsx @@ -19,6 +19,7 @@ const databaseSupport = [ { name: "Elasticsearch", icon: "/icons/database/elasticsearch.svg", tone: "#00bfb3" }, { name: "Doris", icon: "/icons/database/doris.svg", tone: "#5b7cfa" }, { name: "StarRocks", icon: "/icons/database/starrocks.svg", tone: "#6750ff" }, + { name: "Manticore Search", icon: "/icons/database/manticoresearch.png", tone: "#b8e646" }, { name: "Redshift", icon: "/icons/database/redshift.svg", tone: "#8c4fff" }, { name: "Dameng", icon: "/icons/database/dm.svg", tone: "#3857ff" }, { name: "GaussDB", icon: "/icons/database/gaussdb.svg", tone: "#ff5a3d" }, diff --git a/docs/app/[lang]/page.tsx b/docs/app/[lang]/page.tsx index 125df2c2b..cf013304a 100644 --- a/docs/app/[lang]/page.tsx +++ b/docs/app/[lang]/page.tsx @@ -71,6 +71,7 @@ const databaseSupport = [ { name: "Elasticsearch", icon: "/icons/database/elasticsearch.svg", tone: "#00bfb3" }, { name: "Doris", icon: "/icons/database/doris.svg", tone: "#5b7cfa" }, { name: "StarRocks", icon: "/icons/database/starrocks.svg", tone: "#6750ff" }, + { name: "Manticore Search", icon: "/icons/database/manticoresearch.png", tone: "#b8e646" }, { name: "Redshift", icon: "/icons/database/redshift.svg", tone: "#8c4fff" }, { name: "Dameng", icon: "/icons/database/dm.svg", tone: "#3857ff" }, { name: "GaussDB", icon: "/icons/database/gaussdb.svg", tone: "#ff5a3d" }, diff --git a/docs/content/docs/databases.cn.mdx b/docs/content/docs/databases.cn.mdx index 4c30de776..8ec1855ee 100644 --- a/docs/content/docs/databases.cn.mdx +++ b/docs/content/docs/databases.cn.mdx @@ -13,7 +13,7 @@ description: 了解 DBX 可以连接哪些数据库,以及每类高级功能 | ------------------- | ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | | 核心 SQL 数据库 | MySQL、PostgreSQL、SQLite、DuckDB、ClickHouse、SQL Server、Oracle、Redshift、DM、GaussDB、KWDB、Teradata、Vertica、Exasol、Firebird、SAP HANA、崖山 YashanDB、GBase | 直接出现在 DBX 连接选择器中 | | 文档与键值数据库 | Redis、MongoDB、Elasticsearch、etcd | 根据数据模型使用专门浏览器或查询流程,不完全等同于关系型表格 | -| MySQL 兼容类型 | MariaDB、TiDB、OceanBase、Doris、SelectDB、StarRocks、GoldenDB、TDSQL、PolarDB、GreatSQL、自定义 MySQL | 复用 MySQL 风格连接能力 | +| MySQL 兼容类型 | MariaDB、TiDB、OceanBase、Doris、SelectDB、StarRocks、Manticore Search、GoldenDB、TDSQL、PolarDB、GreatSQL、自定义 MySQL | 复用 MySQL 风格连接能力 | | PostgreSQL 兼容类型 | openGauss、KingBase、HighGo、Vastbase、CockroachDB、自定义 PostgreSQL | 复用 PostgreSQL 风格连接能力 | | 文件型数据库 | SQLite、DuckDB、Microsoft Access、RQLite、Turso | 选择本地数据库文件或 HTTP 端点,不填写主机和端口 | | 时序与边缘数据库 | InfluxDB、IoTDB、TDengine、Databend | 面向时序、IoT 或边缘场景优化 | @@ -34,6 +34,7 @@ description: 了解 DBX 可以连接哪些数据库,以及每类高级功能 | TiDB | 4000 | | OceanBase | 2881 | | Doris / SelectDB / StarRocks / Databend | 9030 | +| Manticore Search | 9306 | | Redshift | 5439 | | CockroachDB | 26257 | | DM | 5236 | diff --git a/docs/content/docs/databases.mdx b/docs/content/docs/databases.mdx index 6d0323760..f68cbf0cf 100644 --- a/docs/content/docs/databases.mdx +++ b/docs/content/docs/databases.mdx @@ -13,7 +13,7 @@ The connection dialog exposes ready-to-use profiles with default ports and drive | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | | Core SQL engines | MySQL, PostgreSQL, SQLite, DuckDB, ClickHouse, SQL Server, Oracle, Redshift, DM, GaussDB, KWDB, Teradata, Vertica, Exasol, Firebird, SAP HANA, YashanDB, GBase | Available directly in the DBX connection picker | | Document and key-value engines | Redis, MongoDB, Elasticsearch, etcd | Use dedicated browsers or query flows instead of the generic relational grid where appropriate | -| MySQL-compatible profiles | MariaDB, TiDB, OceanBase, Doris, SelectDB, StarRocks, GoldenDB, TDSQL, PolarDB, GreatSQL, custom MySQL | Reuse MySQL-style connection handling where the engine speaks a compatible protocol | +| MySQL-compatible profiles | MariaDB, TiDB, OceanBase, Doris, SelectDB, StarRocks, Manticore Search, GoldenDB, TDSQL, PolarDB, GreatSQL, 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, RQLite, Turso | Choose a local database file or HTTP endpoint instead of host and port | | Time-series and edge | InfluxDB, IoTDB, TDengine, Databend | Optimized for time-series, IoT, or edge workloads | @@ -34,6 +34,7 @@ The connection dialog exposes ready-to-use profiles with default ports and drive | TiDB | 4000 | | OceanBase | 2881 | | Doris / SelectDB / StarRocks / Databend | 9030 | +| Manticore Search | 9306 | | Redshift | 5439 | | CockroachDB | 26257 | | DM | 5236 | diff --git a/docs/public/icons/database/manticoresearch.png b/docs/public/icons/database/manticoresearch.png new file mode 100644 index 0000000000000000000000000000000000000000..420cb72a596fc8d3a7358c64eed5fdaa3f2efc3b GIT binary patch literal 10184 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 literal 0 HcmV?d00001 diff --git a/packages/app-tests/connectionUrlPlaceholder.test.ts b/packages/app-tests/connectionUrlPlaceholder.test.ts index 45516eaa0..24b8b8f10 100644 --- a/packages/app-tests/connectionUrlPlaceholder.test.ts +++ b/packages/app-tests/connectionUrlPlaceholder.test.ts @@ -6,6 +6,7 @@ const expected: Record = { mysql: "mysql://user:password@host:port/database", doris: "mysql://user:password@host:port/database", starrocks: "mysql://user:password@host:port/database", + manticoresearch: "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", @@ -42,6 +43,7 @@ test("mysql-family types share the same placeholder", () => { const mysql = connectionUrlPlaceholder("mysql"); assert.equal(connectionUrlPlaceholder("doris"), mysql); assert.equal(connectionUrlPlaceholder("starrocks"), mysql); + assert.equal(connectionUrlPlaceholder("manticoresearch"), mysql); }); test("postgres-family types share the same placeholder", () => { diff --git a/packages/app-tests/databaseCapabilities.test.ts b/packages/app-tests/databaseCapabilities.test.ts index d276b6304..9b6f71612 100644 --- a/packages/app-tests/databaseCapabilities.test.ts +++ b/packages/app-tests/databaseCapabilities.test.ts @@ -225,7 +225,9 @@ test("describes feature support through capability helpers", () => { assert.equal(supportsTableStructureEditing("clickhouse"), true); assert.equal(supportsTableStructureEditing("rqlite"), true); assert.equal(supportsTableStructureEditing("mongodb"), false); + assert.equal(supportsTableStructureEditing("manticoresearch"), false); assert.equal(supportsDatabaseCreation("clickhouse"), true); + assert.equal(supportsDatabaseCreation("manticoresearch"), false); assert.equal(supportsDatabaseCreation("sqlite"), false); assert.equal(supportsFieldLineage("gaussdb"), true); assert.equal(supportsFieldLineage("kwdb"), true); @@ -233,15 +235,19 @@ test("describes feature support through capability helpers", () => { assert.equal(supportsTransfer("duckdb"), true); assert.equal(supportsTransfer("hive"), true); assert.equal(supportsTransfer("mongodb"), true); + assert.equal(supportsTransfer("manticoresearch"), false); assert.equal(supportsDriverManagement("oracle"), true); assert.equal(supportsDriverManagement("mysql"), false); + assert.equal(supportsDriverManagement("manticoresearch"), 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("manticoresearch"), false); assert.equal(supportsObjectBrowser("mongodb"), false); assert.equal(supportsTableTruncate("mysql"), true); + assert.equal(supportsTableTruncate("manticoresearch"), false); assert.equal(supportsTableTruncate("duckdb"), false); assert.equal(supportsTableTruncate("rqlite"), false); }); @@ -249,6 +255,7 @@ test("describes feature support through capability helpers", () => { test("loads product support levels and capabilities from the driver manifest", () => { assert.equal(manifestDatabaseTypes().includes("mysql"), true); assert.equal(databaseSupportLevel("mysql"), "operate"); + assert.equal(databaseSupportLevel("manticoresearch"), "browse"); assert.equal(databaseSupportLevel("jdbc"), "browse"); assert.equal(databaseSupportLevel("redis"), "connect"); @@ -266,6 +273,27 @@ test("loads product support levels and capabilities from the driver manifest", ( userAdmin: false, }, ); + + assert.deepEqual( + { + queryExecution: databaseProductCapabilities("manticoresearch").queryExecution, + metadataBrowse: databaseProductCapabilities("manticoresearch").metadataBrowse, + objectBrowser: databaseProductCapabilities("manticoresearch").objectBrowser, + tableDataEdit: databaseProductCapabilities("manticoresearch").tableDataEdit, + tableStructureEdit: databaseProductCapabilities("manticoresearch").tableStructureEdit, + sqlFileExecution: databaseProductCapabilities("manticoresearch").sqlFileExecution, + userAdmin: databaseProductCapabilities("manticoresearch").userAdmin, + }, + { + queryExecution: true, + metadataBrowse: true, + objectBrowser: false, + tableDataEdit: false, + tableStructureEdit: false, + sqlFileExecution: true, + userAdmin: false, + }, + ); }); test("object browser entry follows database tree shape", () => { diff --git a/packages/app-tests/tableStructureCapabilities.test.ts b/packages/app-tests/tableStructureCapabilities.test.ts index fa70c2af7..275abf782 100644 --- a/packages/app-tests/tableStructureCapabilities.test.ts +++ b/packages/app-tests/tableStructureCapabilities.test.ts @@ -94,7 +94,7 @@ test("limited analytic engines can open the editor for supported operations only }); test("unsupported non-relational databases do not open the structure editor", () => { - for (const dbType of ["redis", "mongodb", "elasticsearch", "neo4j", undefined] as const) { + for (const dbType of ["redis", "mongodb", "elasticsearch", "manticoresearch", "neo4j", undefined] as const) { const caps = getTableStructureCapabilities(dbType); assert.equal(caps.dialect, "unsupported"); assert.equal(canEditTableStructure(dbType), false); diff --git a/packages/app-tests/visibleDatabases.test.ts b/packages/app-tests/visibleDatabases.test.ts index 8f07d9ebd..4b25f99dd 100644 --- a/packages/app-tests/visibleDatabases.test.ts +++ b/packages/app-tests/visibleDatabases.test.ts @@ -29,8 +29,10 @@ test("normalizes selected database names against fresh database names", () => { test("mysql system databases are hidden by default but can be explicitly selected", () => { const databases = ["app", "information_schema", "mysql", "performance_schema", "sys"]; assert.deepEqual(filterDatabaseNamesForConnection(databases, { db_type: "mysql" }), ["app"]); + assert.deepEqual(filterDatabaseNamesForConnection(databases, { db_type: "manticoresearch" }), ["app"]); assert.deepEqual(filterDatabaseNamesForConnection(databases, { db_type: "mysql", visible_databases: ["app", "sys"] }), ["app", "sys"]); assert.equal(isSystemDatabaseName("mysql", "performance_schema"), true); + assert.equal(isSystemDatabaseName("manticoresearch", "information_schema"), true); assert.equal(isSystemDatabaseName("postgres", "information_schema"), false); }); diff --git a/packages/mcp-server/src/index.ts b/packages/mcp-server/src/index.ts index fffe58f99..e51ed0d4b 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, etcd, doris, starrocks, redshift, dameng, kingbase, highgo, vastbase, goldendb, databend, gaussdb, kwdb, yashandb, databricks, saphana, teradata, vertica, firebird, exasol, opengauss, oceanbase-oracle, gbase, h2, snowflake, trino, hive, db2, informix, influxdb, iris, neo4j, cassandra, bigquery, kylin, sundb, tdengine, iotdb, xugu, jdbc, access"; + "Database type: postgres, mysql, sqlite, rqlite, redis, duckdb, clickhouse, sqlserver, mongodb, oracle, elasticsearch, etcd, doris, starrocks, manticoresearch, redshift, dameng, kingbase, highgo, vastbase, goldendb, databend, gaussdb, kwdb, yashandb, databricks, saphana, teradata, vertica, firebird, exasol, opengauss, oceanbase-oracle, gbase, h2, snowflake, trino, hive, db2, informix, influxdb, iris, neo4j, cassandra, bigquery, kylin, sundb, tdengine, iotdb, xugu, jdbc, access"; const FILE_CAPABLE_CONNECTION_TYPES = new Set(["sqlite", "duckdb", "access", "h2"]); export function createDbxMcpServer(backend: Backend, options: { isWebMode?: boolean } = {}): McpServer { diff --git a/packages/node-core/src/database.ts b/packages/node-core/src/database.ts index 2d6b2491a..d4859cedf 100644 --- a/packages/node-core/src/database.ts +++ b/packages/node-core/src/database.ts @@ -284,7 +284,7 @@ function portBytes(port: number): Buffer { } function isMysqlType(dbType: string): boolean { - return dbType === "mysql" || dbType === "doris" || dbType === "starrocks"; + return dbType === "mysql" || dbType === "doris" || dbType === "starrocks" || dbType === "manticoresearch"; } interface BridgeQueryResult { diff --git a/packages/node-core/src/diagnostics.ts b/packages/node-core/src/diagnostics.ts index a8f7dff79..859cfa7e7 100644 --- a/packages/node-core/src/diagnostics.ts +++ b/packages/node-core/src/diagnostics.ts @@ -2,7 +2,7 @@ import { access, readFile } from "node:fs/promises"; import { bridgePortFilePath, dbPath, appDataDir } from "./paths.js"; import { inspectConnectionStore } from "./connections.js"; -export const DIRECT_QUERY_TYPES = ["postgres", "redshift", "mysql", "doris", "starrocks", "sqlite", "rqlite", "gaussdb", "kwdb", "opengauss"] as const; +export const DIRECT_QUERY_TYPES = ["postgres", "redshift", "mysql", "doris", "starrocks", "manticoresearch", "sqlite", "rqlite", "gaussdb", "kwdb", "opengauss"] as const; export type DirectQueryType = (typeof DIRECT_QUERY_TYPES)[number]; diff --git a/packages/node-core/tests/driver-manifest.test.ts b/packages/node-core/tests/driver-manifest.test.ts index 6ab514223..c76a3d6cf 100644 --- a/packages/node-core/tests/driver-manifest.test.ts +++ b/packages/node-core/tests/driver-manifest.test.ts @@ -57,6 +57,11 @@ test("runtime direct query routing matches diagnostic direct query types", () => } }); +test("Manticore Search is direct-query capable", () => { + assert.equal(isDirectQueryType("manticoresearch"), true); + assert.equal(DIRECT_QUERY_TYPES.includes("manticoresearch" as any), true); +}); + test("driver manifest declares support levels and product capabilities", () => { const manifest = loadManifest(); @@ -72,4 +77,14 @@ test("driver manifest declares support levels and product capabilities", () => { assert.equal(jdbc?.supportLevel, "browse"); assert.equal(jdbc?.capabilities.metadataBrowse, true); assert.equal(jdbc?.capabilities.tableStructureEdit, false); + + const manticore = manifest.drivers.find((driver) => driver.dbType === "manticoresearch"); + assert.equal(manticore?.supportLevel, "browse"); + assert.equal(manticore?.capabilities.queryExecution, true); + assert.equal(manticore?.capabilities.metadataBrowse, true); + assert.equal(manticore?.capabilities.objectBrowser, false); + assert.equal(manticore?.capabilities.tableDataEdit, false); + assert.equal(manticore?.capabilities.tableStructureEdit, false); + assert.equal(manticore?.capabilities.databaseCreate, false); + assert.equal(manticore?.capabilities.userAdmin, false); }); diff --git a/src-tauri/src/commands/connection.rs b/src-tauri/src/commands/connection.rs index f0e2993c8..e1b821a0c 100644 --- a/src-tauri/src/commands/connection.rs +++ b/src-tauri/src/commands/connection.rs @@ -273,7 +273,7 @@ pub async fn test_connection(state: State<'_, Arc>, config: Connection Err(e) => Err(e), } } - DatabaseType::Doris | DatabaseType::StarRocks => { + DatabaseType::Doris | DatabaseType::StarRocks | DatabaseType::ManticoreSearch => { match db::mysql::connect_bare(&url, connect_timeout).await { Ok(pool) => { let _ = pool.disconnect().await; @@ -488,7 +488,7 @@ pub async fn connect_db(state: State<'_, Arc>, config: ConnectionConfi connect_mysql_metadata_pool(&config, &db_config, &host, port, connect_timeout, 3).await?; PoolKind::Mysql(pool, mode) } - DatabaseType::Doris | DatabaseType::StarRocks => PoolKind::Mysql( + DatabaseType::Doris | DatabaseType::StarRocks | DatabaseType::ManticoreSearch => PoolKind::Mysql( connect_bare_metadata_pool(&db_config, &host, port, connect_timeout, 3).await?, MysqlMode::Bare, ),