添加 Manticore Search 支持 (#1145)

* 添加 Manticore Search 支持

* 更新 Manticore Search 文档
This commit is contained in:
Vergil Lai 2026-06-13 20:23:36 +08:00 committed by GitHub
parent 6c57169162
commit 799d20a2cf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 258 additions and 49 deletions

View File

@ -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
<details>
<summary><strong>What databases are supported?</strong></summary>
MySQL, PostgreSQL, SQLite, Redis, MongoDB, DuckDB, ClickHouse, SQL Server, Oracle, Elasticsearch, MariaDB, TiDB, OceanBase, openGauss, GaussDB, 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.
</details>
<details>

View File

@ -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 和
<details>
<summary><strong>支持哪些数据库?</strong></summary>
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 连接。
</details>
<details>

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

@ -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<string, string> = {
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<DbCategory[]>(() => [{ key: "all", title: "", options: dbOptions }]);
@ -867,6 +879,7 @@ const sqliteExtensionPaths = computed({
const tlsCapableDatabaseTypes = new Set<DatabaseType>(["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) {
</label>
</div>
<div class="grid grid-cols-4 items-center gap-4">
<div v-if="supportsGenericUrlParams" class="grid grid-cols-4 items-center gap-4">
<Label class="text-right">{{ t("connection.urlParams") }}</Label>
<Input
v-model="form.url_params"

View File

@ -52,6 +52,7 @@ const assetIcons: Record<string, string> = {
bigquery: "bigquery",
cassandra: "cassandra",
doris: "doris",
manticoresearch: "manticoresearch.png",
selectdb: "selectdb",
tdengine: "tdengine",
starrocks: "starrocks",

View File

@ -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(" · ");
}
}

View File

@ -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);
}
}

View File

@ -4,6 +4,7 @@ const SYSTEM_DATABASE_RULES: Partial<Record<DatabaseType, ReadonlySet<string>>>
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));
}
}

View File

@ -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);
},
};
});
});

View File

@ -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[];
}
}

View File

@ -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",

View File

@ -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?

View File

@ -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")),
}
}
}

View File

@ -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),

View File

@ -319,7 +319,9 @@ async fn list_databases_once(state: &AppState, connection_id: &str) -> Result<Ve
match pool {
PoolKind::Mysql(p, _) if db_config.as_ref().is_some_and(is_doris_family_config) => {
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<_>>(), 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<_>>(),
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<db::DatabaseInfo>,
config: Option<&ConnectionConfig>,
) -> Vec<db::DatabaseInfo> {
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 {

View File

@ -7,7 +7,9 @@ pub(in crate::schema) async fn list_databases(
config: Option<&ConnectionConfig>,
) -> Result<Vec<db::DatabaseInfo>, 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<db::DatabaseInfo>,
config: Option<&ConnectionConfig>,
) -> Vec<db::DatabaseInfo> {
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")
}

View File

@ -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> {

View File

@ -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(),

View File

@ -36,6 +36,7 @@ pub fn quote_table_identifier(database_type: Option<DatabaseType>, name: &str) -
DatabaseType::Mysql
| DatabaseType::Goldendb
| DatabaseType::StarRocks
| DatabaseType::ManticoreSearch
| DatabaseType::Hive
| DatabaseType::Databend
| DatabaseType::Tdengine

View File

@ -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",

View File

@ -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" },

View File

@ -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" },

View File

@ -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 |

View File

@ -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 |

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

@ -6,6 +6,7 @@ const expected: Record<string, string> = {
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", () => {

View File

@ -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", () => {

View File

@ -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);

View File

@ -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);
});

View File

@ -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 {

View File

@ -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 {

View File

@ -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];

View File

@ -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);
});

View File

@ -273,7 +273,7 @@ pub async fn test_connection(state: State<'_, Arc<AppState>>, 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<AppState>>, 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,
),