feat(spark): add Apache Spark database driver with catalog support

* feat(spark): add Apache Spark database driver

Spark Thrift Server speaks the HiveServer2 protocol, so the Spark driver
reuses the hive-jdbc agent runtime. Registers spark (agentKey=spark,
defaultPort=10015) across the manifest, DatabaseType enum, agent catalog,
SQL dialects, connection dialog, and a SparkAgent built on AbstractJdbcAgent.

* feat(spark): add catalog support and official logo

SparkAgent now supports Spark 3.4+ multi-catalog (Paimon, Lance, Iceberg)
via catalog=<name> in url_params, mirroring the StarRocks catalog flow:
- afterConnect switches to the configured catalog via USE <catalog>
- listDatabases returns the catalog name when SHOW DATABASES is empty
  (catalogs like Lance expose tables at the catalog root with no databases)
- listTables uses SHOW TABLES IN <catalog>[.<schema>] uniformly, working
  for both catalog-root tables (Lance) and catalog.schema tables (Paimon)
- setSchemaSQL qualifies USE with the catalog to prevent resetting to
  spark_catalog on schema switches
- buildJdbcUrl omits database from the URL path

Also replaces the placeholder lightning icon with the official Apache Spark
logo (spark-logo.png from spark.apache.org).

* fix(spark): use catalog-qualified DESCRIBE for column retrieval

getColumnsFromDescribe used USE <schema> + DESCRIBE <table>, which
generated USE `catalog`.`catalog` when the sidebar passed the catalog
name as the schema (Lance case where the catalog is the only node).
This failed and fell back to JDBC metadata which also returned nothing,
resulting in an empty DDL (CREATE TABLE ... ()).

Now uses DESCRIBE `catalog`.`table` directly when a catalog is
configured, bypassing USE entirely. Also fixes setSchemaSQL to treat
schema == catalog as catalog-only (no .schema suffix).

* fix(ci): add spark to metadata coverage matrix and BRIDGE_REQUIRED_TYPES

- agents/metadata-constraint-coverage.tsv: register spark as
  intentional-fallback (SHOW TABLES/SHOW DATABASES, like Hive/TDengine)
- packages/node-core/src/diagnostics.ts: add spark to
  BRIDGE_REQUIRED_TYPES (mcpMode=bridge, matches manifest)

* fix(ci): add spark to DBX_CONNECTION_TYPE_DESCRIPTION in mcp-server

The driver-manifest test asserts every manifest dbType appears in the
MCP server's connection type description string.
This commit is contained in:
jischeng
2026-07-07 14:36:22 +08:00
committed by GitHub
parent 586faf28c8
commit c3893bf314
24 changed files with 385 additions and 4 deletions
+11
View File
@@ -0,0 +1,11 @@
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'org.apache.hive:hive-jdbc:4.0.1:standalone'
implementation 'org.slf4j:slf4j-nop:1.7.30'
}
tasks.named('shadowJar') {
manifest {
attributes('Agent-Label': 'Apache Spark', 'Main-Class': 'com.dbx.agent.spark.SparkAgent')
}
}
@@ -0,0 +1,308 @@
package com.dbx.agent.spark;
import com.dbx.agent.AbstractJdbcAgent;
import com.dbx.agent.ColumnInfo;
import com.dbx.agent.ConnectParams;
import com.dbx.agent.DatabaseInfo;
import com.dbx.agent.ForeignKeyInfo;
import com.dbx.agent.IndexInfo;
import com.dbx.agent.JdbcIdentifiers;
import com.dbx.agent.JsonRpcServer;
import com.dbx.agent.TableInfo;
import com.dbx.agent.TriggerInfo;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
// Spark Thrift Server speaks the HiveServer2 protocol, so the Spark agent reuses
// the Hive JDBC driver and mirrors the Hive agent's metadata queries (SHOW
// DATABASES / SHOW TABLES / DESCRIBE), which Spark SQL supports as well.
//
// Spark 3.4+ supports multiple catalogs (Paimon, Lance, Iceberg, ...). When the
// user supplies `catalog=<name>` in url_params, the agent switches to that catalog
// after connecting and uses `SHOW TABLES IN <catalog>` for listing — which works
// uniformly for catalogs that have databases (Paimon) and those that expose tables
// at the catalog root with no databases (Lance). Mirrors the StarRocks catalog flow.
public final class SparkAgent extends AbstractJdbcAgent {
private String configuredCatalog;
@Override
protected String driverClass() {
return "org.apache.hive.jdbc.HiveDriver";
}
@Override
protected String buildJdbcUrl(ConnectParams params) {
return "jdbc:hive2://" + params.getHost() + ":" + params.getPort() + "/";
}
@Override
protected void afterConnect(ConnectParams params, Connection connection) throws Exception {
String catalog = extractCatalogParam(params.getUrl_params());
configuredCatalog = catalog;
if (catalog != null && !catalog.isEmpty()) {
try (java.sql.Statement stmt = connection.createStatement()) {
stmt.execute("USE " + JdbcIdentifiers.INSTANCE.backtick(catalog));
}
}
}
@Override
public List<DatabaseInfo> listDatabases() {
return unchecked(() -> {
List<DatabaseInfo> result = new ArrayList<>();
try (java.sql.Statement stmt = requireConnected().createStatement();
ResultSet rs = stmt.executeQuery("SHOW DATABASES")) {
while (rs.next()) {
result.add(new DatabaseInfo(rs.getString(1)));
}
}
// Catalogs like Lance expose tables at the catalog root with no
// databases — SHOW DATABASES returns nothing. Return the catalog
// name itself as the database node so the sidebar can drill in.
if (result.isEmpty() && configuredCatalog != null && !configuredCatalog.isEmpty()) {
result.add(new DatabaseInfo(configuredCatalog));
}
result.sort(Comparator.comparing(DatabaseInfo::getName));
return result;
});
}
@Override
public List<String> listSchemas() {
List<String> result = new ArrayList<>();
for (DatabaseInfo database : listDatabases()) {
result.add(database.getName());
}
return result;
}
@Override
public List<TableInfo> listTables(String schema) {
return unchecked(() -> {
List<TableInfo> result = new ArrayList<>();
String sql = buildShowTablesSql(schema);
try (java.sql.Statement stmt = requireConnected().createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
while (rs.next()) {
String name = readTableName(rs);
if (name != null && !name.isEmpty()) {
result.add(new TableInfo(name, "TABLE", null));
}
}
}
result.sort(Comparator.comparing(TableInfo::getName));
return result;
});
}
// With a catalog configured, use `SHOW TABLES IN <catalog>[.<schema>]` which
// works for both catalog-root tables (Lance) and catalog.schema tables (Paimon).
// Without a catalog, fall back to the Hive-style `USE <schema>` + SHOW TABLES.
private String buildShowTablesSql(String schema) throws Exception {
if (configuredCatalog != null && !configuredCatalog.isEmpty()) {
String target = JdbcIdentifiers.INSTANCE.backtick(configuredCatalog);
if (schema != null && !schema.isEmpty() && !schema.equals(configuredCatalog)) {
target += "." + JdbcIdentifiers.INSTANCE.backtick(schema);
}
return "SHOW TABLES IN " + target;
}
useSchema(schema);
return "SHOW TABLES";
}
// `SHOW TABLES` (Hive style) returns a single tabName column, while
// `SHOW TABLES IN <catalog>` (Spark SQL) returns (database, tableName,
// isTemporary). Pick tableName from whichever column it lives in.
private static String readTableName(ResultSet rs) throws Exception {
int columns = rs.getMetaData().getColumnCount();
return columns > 1 ? rs.getString(2) : rs.getString(1);
}
@Override
public List<ColumnInfo> getColumns(String schema, String table) {
return unchecked(() -> {
try {
return getColumnsFromDescribe(schema, table);
} catch (Exception ignored) {
return getColumnsFromMetadata(requireConnected(), schema, table);
}
});
}
@Override
public List<IndexInfo> listIndexes(String schema, String table) {
return Collections.emptyList();
}
@Override
public List<ForeignKeyInfo> listForeignKeys(String schema, String table) {
return Collections.emptyList();
}
@Override
public List<TriggerInfo> listTriggers(String schema, String table) {
return Collections.emptyList();
}
@Override
public String setSchemaSQL(String schema) {
// When a catalog is configured, qualify the USE statement with the
// catalog so switching schemas does not reset the catalog back to the
// default (spark_catalog).
if (configuredCatalog != null && !configuredCatalog.isEmpty()) {
if (schema == null || schema.isEmpty() || schema.equals(configuredCatalog)) {
return "USE " + JdbcIdentifiers.INSTANCE.backtick(configuredCatalog);
}
return "USE " + JdbcIdentifiers.INSTANCE.backtick(configuredCatalog)
+ "." + JdbcIdentifiers.INSTANCE.backtick(schema);
}
return "USE " + JdbcIdentifiers.INSTANCE.backtick(schema);
}
private static String extractCatalogParam(String urlParams) {
if (urlParams == null || urlParams.isEmpty()) {
return null;
}
for (String segment : urlParams.split("&")) {
int eq = segment.indexOf('=');
if (eq <= 0) {
continue;
}
if (segment.substring(0, eq).trim().equalsIgnoreCase("catalog")) {
String value = segment.substring(eq + 1).trim();
return value.isEmpty() ? null : value;
}
}
return null;
}
@Override
protected Object resultValue(ResultSet rs, int index, int sqlType) {
return unchecked(() -> {
Object value = rs.getObject(index);
return rs.wasNull() ? null : value == null ? null : value.toString();
});
}
private void useSchema(String schema) throws Exception {
try (java.sql.Statement stmt = requireConnected().createStatement()) {
stmt.execute(setSchemaSQL(schema));
}
}
private List<ColumnInfo> getColumnsFromDescribe(String schema, String table) throws Exception {
List<ColumnInfo> result = new ArrayList<>();
// Use catalog-qualified DESCRIBE when a catalog is configured, avoiding
// USE <schema> which would generate USE `catalog`.`catalog` when the
// sidebar passes the catalog name as the schema (Lance case).
String describeTarget;
if (configuredCatalog != null && !configuredCatalog.isEmpty()) {
describeTarget = JdbcIdentifiers.INSTANCE.backtick(configuredCatalog);
if (schema != null && !schema.isEmpty() && !schema.equals(configuredCatalog)) {
describeTarget += "." + JdbcIdentifiers.INSTANCE.backtick(schema);
}
describeTarget += "." + JdbcIdentifiers.INSTANCE.backtick(table);
} else {
useSchema(schema);
describeTarget = JdbcIdentifiers.INSTANCE.backtick(table);
}
try (java.sql.Statement stmt = requireConnected().createStatement();
ResultSet rs = stmt.executeQuery("DESCRIBE " + describeTarget)) {
while (rs.next()) {
String colName = trimToNull(rs.getString(1));
if (colName == null || colName.startsWith("#")) {
continue;
}
result.add(new ColumnInfo(
colName,
trimToEmpty(rs.getString(2)),
true,
null,
false,
null,
optionalComment(rs),
null,
null,
null
));
}
}
return result;
}
private static List<ColumnInfo> getColumnsFromMetadata(Connection conn, String schema, String table) throws Exception {
List<ColumnInfo> result = new ArrayList<>();
DatabaseMetaData meta = conn.getMetaData();
try (ResultSet rs = meta.getColumns(null, trimToNull(schema), table, "%")) {
while (rs.next()) {
String name = rs.getString("COLUMN_NAME");
if (trimToNull(name) == null) {
continue;
}
result.add(new ColumnInfo(
name,
trimToEmpty(rs.getString("TYPE_NAME")),
rs.getInt("NULLABLE") != DatabaseMetaData.columnNoNulls,
rs.getString("COLUMN_DEF"),
false,
null,
rs.getString("REMARKS"),
intOrNull(rs, "COLUMN_SIZE"),
intOrNull(rs, "DECIMAL_DIGITS"),
characterLength(rs)
));
}
}
return result;
}
private static String trimToNull(String value) {
if (value == null) {
return null;
}
String trimmed = value.trim();
return trimmed.isEmpty() ? null : trimmed;
}
private static String trimToEmpty(String value) {
return value == null ? "" : value.trim();
}
private static Integer intOrNull(ResultSet rs, String column) throws Exception {
Object value = rs.getObject(column);
return value instanceof Number ? ((Number) value).intValue() : null;
}
private static Integer characterLength(ResultSet rs) throws Exception {
String typeName = rs.getString("TYPE_NAME");
if (typeName == null) {
return null;
}
String normalized = typeName.toLowerCase(Locale.ROOT);
if (!normalized.contains("char") && !normalized.contains("text") && !normalized.contains("string")) {
return null;
}
return intOrNull(rs, "COLUMN_SIZE");
}
private static String optionalComment(ResultSet rs) {
return unchecked(() -> {
try {
String comment = trimToNull(rs.getString(3));
return comment;
} catch (Exception e) {
return null;
}
});
}
public static void main(String[] args) {
new JsonRpcServer(new SparkAgent()).run();
}
}
+1
View File
@@ -17,6 +17,7 @@ neo4j intentional-fallback java-graph-metadata Uses JDBC metadata with Cypher fa
oceanbase-oracle native-pushdown java-sql Uses Oracle-compatible metadata SQL with type, filter, stable order, and ROWNUM paging.
oracle-go native-pushdown native-go Uses Oracle-compatible metadata SQL with type, filter, stable order, and ROWNUM paging, with legacy profile guard.
snowflake native-pushdown java-sql Uses INFORMATION_SCHEMA with type, filter, stable order, and literal LIMIT/OFFSET.
spark intentional-fallback java-show-metadata Uses SHOW TABLES/SHOW DATABASES; stable server-side fuzzy paging is not portable, common constraints filter locally.
sundb native-pushdown java-sql Uses information_schema with type, filter, stable order, and LIMIT/OFFSET.
tdengine intentional-fallback java-show-metadata Uses SHOW STABLES/TABLES; stable server-side fuzzy paging is not portable, common constraints filter locally.
trino native-pushdown java-sql Uses information_schema.tables with type, filter, stable order, and literal LIMIT/OFFSET.
1 driver strategy scope reason
17 oceanbase-oracle native-pushdown java-sql Uses Oracle-compatible metadata SQL with type, filter, stable order, and ROWNUM paging.
18 oracle-go native-pushdown native-go Uses Oracle-compatible metadata SQL with type, filter, stable order, and ROWNUM paging, with legacy profile guard.
19 snowflake native-pushdown java-sql Uses INFORMATION_SCHEMA with type, filter, stable order, and literal LIMIT/OFFSET.
20 spark intentional-fallback java-show-metadata Uses SHOW TABLES/SHOW DATABASES; stable server-side fuzzy paging is not portable, common constraints filter locally.
21 sundb native-pushdown java-sql Uses information_schema with type, filter, stable order, and LIMIT/OFFSET.
22 tdengine intentional-fallback java-show-metadata Uses SHOW STABLES/TABLES; stable server-side fuzzy paging is not portable, common constraints filter locally.
23 trino native-pushdown java-sql Uses information_schema.tables with type, filter, stable order, and literal LIMIT/OFFSET.
+1 -1
View File
@@ -4,7 +4,7 @@ def infrastructureModules = ['common', 'test-support']
def driverModules = [
'access', 'dameng', 'kingbase', 'vastbase', 'goldendb', 'databend', 'databricks', 'saphana',
'teradata', 'vertica', 'firebird', 'exasol', 'oceanbase-oracle', 'gbase8a', 'gbase8s',
'bigquery', 'kylin', 'sundb', 'h2', 'snowflake', 'trino', 'hive',
'bigquery', 'kylin', 'sundb', 'h2', 'snowflake', 'trino', 'hive', 'spark',
'db2', 'informix', 'neo4j', 'cassandra', 'mongodb', 'highgo', 'tdengine', 'yashandb', 'oscar',
'iris', 'iotdb', 'etcd', 'zookeeper', 'kafka'
]
+1
View File
@@ -13,6 +13,7 @@
"goldendb": "0.1.28",
"h2": "0.1.28",
"hive": "0.1.28",
"spark": "0.1.0",
"informix": "0.1.29",
"kingbase": "0.1.28",
"kylin": "0.1.28",
Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

@@ -612,6 +612,7 @@ const driverProfiles: Record<
trino: { type: "trino", port: 8080, user: "", label: "Trino", icon: "trino" },
prestosql: { type: "prestosql", port: 8080, user: "", label: "PrestoSQL", icon: "presto" },
hive: { type: "hive", port: 10000, user: "", label: "Apache Hive", icon: "hive" },
spark: { type: "spark", port: 10015, user: "", label: "Apache Spark", icon: "spark-logo.png" },
db2: { type: "db2", port: 50000, user: "db2inst1", label: "IBM DB2", icon: "db2" },
informix: { type: "informix", port: 9088, user: "informix", label: "Informix", icon: "informix" },
dremio: { type: "jdbc", port: 31010, user: "", label: "Dremio", icon: "dremio" },
@@ -1524,6 +1525,7 @@ const iconTypeMap: Record<string, string> = {
trino: "trino",
prestosql: "prestosql",
hive: "hive",
spark: "spark-logo.png",
db2: "db2",
informix: "informix",
dremio: "dremio",
@@ -1593,6 +1595,7 @@ const dbOptions: DbOption[] = [
{ value: "trino", label: "Trino" },
{ value: "prestosql", label: "PrestoSQL" },
{ value: "hive", label: "Hive" },
{ value: "spark", label: "Apache Spark" },
{ value: "db2", label: "DB2" },
{ value: "informix", label: "Informix" },
{ value: "neo4j", label: "Neo4j" },
@@ -4422,7 +4425,9 @@ function openExternalUrl(url: string) {
? 'OAuthType=0;OAuthServiceAcctEmail=svc@project.iam.gserviceaccount.com;OAuthPvtKeyPath=/path/key.json'
: form.db_type === 'informix'
? 'CLIENT_LOCALE=en_US.utf8;DB_LOCALE=en_US.utf8'
: 'sslmode=disable'
: form.db_type === 'spark'
? 'catalog=paimon_catalog'
: 'sslmode=disable'
"
/>
</div>
@@ -47,6 +47,7 @@ const assetIcons: Record<string, string> = {
presto: "presto",
prestosql: "presto",
hive: "hive",
spark: "spark-logo.png",
apache_kylin: "apache_kylin",
sundb: "sundb",
trino: "trino",
@@ -29,6 +29,7 @@ const DRIVER_STARTUP_FLOOR_TYPES = new Set<DatabaseType>([
"prestosql",
"jdbc",
"hive",
"spark",
"db2",
"informix",
"neo4j",
@@ -26,6 +26,7 @@ export const SCHEMA_AWARE_TYPES = new Set<DatabaseType>([
"trino",
"prestosql",
"hive",
"spark",
"databend",
"db2",
"informix",
@@ -60,6 +60,7 @@ export const DATABASE_NAMESPACE_CREATION_MATRIX = {
trino: { database: "schema" },
prestosql: { database: "schema" },
hive: { deferred: "Hive database creation needs agent metadata validation first" },
spark: { deferred: "Spark database creation needs agent metadata validation first" },
db2: { database: "schema" },
informix: { database: "schema" },
neo4j: { deferred: "database creation depends on edition/admin privileges" },
@@ -40,6 +40,7 @@ const DATABASE_TYPE_OBJECTS = new Map<DatabaseType, SidebarObjectKind[]>([
["doris", TABLE_VIEW_OBJECTS],
["starrocks", TABLE_VIEW_OBJECTS],
["hive", TABLE_VIEW_OBJECTS],
["spark", TABLE_VIEW_OBJECTS],
["trino", TABLE_VIEW_OBJECTS],
["prestosql", TABLE_VIEW_OBJECTS],
["cassandra", TABLE_VIEW_OBJECTS],
@@ -61,6 +61,7 @@ export const DATABASE_PROPERTY_EDITING_MATRIX = {
trino: { deferred: "catalog properties are connector-managed" },
prestosql: { deferred: "catalog properties are connector-managed" },
hive: { deferred: "database properties need agent metadata validation first" },
spark: { deferred: "database properties need agent metadata validation first" },
db2: { deferred: "schema properties need product-specific handling" },
informix: { deferred: "schema properties need product-specific handling" },
neo4j: { deferred: "database properties depend on edition/admin privileges" },
+1 -1
View File
@@ -23,7 +23,7 @@ export function quoteTableIdentifier(databaseType: DatabaseType | undefined, nam
// JDBC connections use the driver-reported identifier quote string
// (DatabaseMetaData.getIdentifierQuoteString()) — pass through unquoted.
if (databaseType === "jdbc") return name;
if (databaseType === "mysql" || databaseType === "clickhouse" || databaseType === "hive" || databaseType === "databend" || databaseType === "tdengine" || databaseType === "access") return `\`${name.replace(/`/g, "``")}\``;
if (databaseType === "mysql" || databaseType === "clickhouse" || databaseType === "hive" || databaseType === "spark" || databaseType === "databend" || databaseType === "tdengine" || databaseType === "access") return `\`${name.replace(/`/g, "``")}\``;
if (databaseType === "informix" && /^[A-Za-z_][A-Za-z0-9_$]*$/.test(name)) return name;
if (databaseType === "neo4j") return quoteCypherIdentifier(name);
if (databaseType === "sqlserver") return `[${name.replace(/\]/g, "]]")}]`;
@@ -787,6 +787,7 @@ export const useConnectionStore = defineStore("connection", () => {
trino: "Trino",
prestosql: "PrestoSQL",
hive: "Hive",
spark: "Apache Spark",
db2: "DB2",
informix: "Informix",
neo4j: "Neo4j",
+1
View File
@@ -44,6 +44,7 @@ export type DatabaseType =
| "trino"
| "prestosql"
| "hive"
| "spark"
| "db2"
| "informix"
| "neo4j"
@@ -1820,6 +1820,36 @@
"userAdmin": false,
"driverManagement": false
}
},
{
"dbType": "spark",
"label": "Apache Spark",
"runtimeMode": "agent",
"mcpMode": "bridge",
"agentKey": "spark",
"singleConnectionPool": false,
"metadataConnectionScoped": false,
"skipTcpProbe": true,
"defaultPort": 10015,
"supportLevel": "operate",
"capabilities": {
"queryExecution": true,
"metadataBrowse": true,
"objectBrowser": true,
"objectSource": false,
"schemaSearch": true,
"diagram": false,
"tableDataEdit": true,
"tableStructureEdit": false,
"tableImport": false,
"dataTransfer": true,
"sqlFileExecution": true,
"databaseCreate": false,
"fieldLineage": false,
"sqlExplain": false,
"userAdmin": false,
"driverManagement": true
}
}
]
}
+7
View File
@@ -173,6 +173,13 @@ const AGENT_CATALOG: &[AgentCatalogEntry] = &[
store_visible: true,
profiles: &[],
},
AgentCatalogEntry {
db_type: DatabaseType::Spark,
key: "spark",
label: "Apache Spark",
store_visible: true,
profiles: &[],
},
AgentCatalogEntry { db_type: DatabaseType::Db2, key: "db2", label: "IBM DB2", store_visible: true, profiles: &[] },
AgentCatalogEntry {
db_type: DatabaseType::Informix,
+1
View File
@@ -143,6 +143,7 @@ macro_rules! agent_connection_pool_database_type {
| DatabaseType::Snowflake
| DatabaseType::Trino
| DatabaseType::Hive
| DatabaseType::Spark
| DatabaseType::Db2
| DatabaseType::Informix
| DatabaseType::Neo4j
+5
View File
@@ -337,6 +337,7 @@ pub enum DatabaseType {
#[serde(rename = "prestosql")]
PrestoSql,
Hive,
Spark,
#[serde(rename = "db2")]
Db2,
Informix,
@@ -845,6 +846,7 @@ impl ConnectionConfig {
DatabaseType::Trino => format!("trino://{host}:{port}{db_part}"),
DatabaseType::PrestoSql => format!("prestosql://{host}:{port}{db_part}"),
DatabaseType::Hive => format!("hive://{host}:{port}{db_part}"),
DatabaseType::Spark => format!("spark://{host}:{port}{db_part}"),
DatabaseType::Db2 => format!("db2://{host}:{port}{db_part}"),
DatabaseType::Informix => format!("informix://{host}:{port}{db_part}"),
DatabaseType::Neo4j => format!("neo4j://{host}:{port}{db_part}"),
@@ -1034,6 +1036,9 @@ impl ConnectionConfig {
DatabaseType::Hive => {
format!("hive://{}:{}@{host}:{port}{db_part}", username, password)
}
DatabaseType::Spark => {
format!("spark://{}:{}@{host}:{port}{db_part}", username, password)
}
DatabaseType::Db2 => {
format!("db2://{}:{}@{host}:{port}{db_part}", username, password)
}
@@ -52,6 +52,7 @@ pub fn is_schema_aware(database_type: DatabaseType) -> bool {
| DatabaseType::Trino
| DatabaseType::PrestoSql
| DatabaseType::Hive
| DatabaseType::Spark
| DatabaseType::Db2
| DatabaseType::Tdengine
| DatabaseType::Xugu
@@ -86,6 +86,7 @@ pub fn quote_table_identifier(database_type: Option<DatabaseType>, name: &str) -
| DatabaseType::StarRocks
| DatabaseType::ManticoreSearch
| DatabaseType::Hive
| DatabaseType::Spark
| DatabaseType::Databend
| DatabaseType::Tdengine
| DatabaseType::Access
@@ -119,6 +120,7 @@ pub(crate) fn quote_transfer_identifier(name: &str, database_type: &DatabaseType
| DatabaseType::Doris
| DatabaseType::StarRocks
| DatabaseType::Hive
| DatabaseType::Spark
| DatabaseType::Questdb => format!("`{}`", name.replace('`', "``")),
DatabaseType::SqlServer => format!("[{}]", name.replace(']', "]]")),
_ => format!("\"{}\"", name.replace('\"', "\"\"")),
+1 -1
View File
@@ -68,7 +68,7 @@ function formatRedisCommandToolResult(result: RedisCommandResult) {
}
export const DBX_CONNECTION_TYPE_DESCRIPTION =
"Database type: postgres, mysql, sqlite, rqlite, redis, duckdb, clickhouse, sqlserver, mongodb, oracle, elasticsearch, etcd, doris, starrocks, manticoresearch, milvus, qdrant, weaviate, chromadb, redshift, dameng, kingbase, highgo, vastbase, goldendb, databend, gaussdb, kwdb, yashandb, databricks, saphana, teradata, vertica, firebird, exasol, opengauss, oceanbase-oracle, questdb, gbase, h2, snowflake, trino, prestosql, hive, db2, informix, influxdb, iris, neo4j, cassandra, bigquery, kylin, sundb, oscar, tdengine, iotdb, xugu, zookeeper, jdbc, access, mq";
"Database type: postgres, mysql, sqlite, rqlite, redis, duckdb, clickhouse, sqlserver, mongodb, oracle, elasticsearch, etcd, doris, starrocks, manticoresearch, milvus, qdrant, weaviate, chromadb, redshift, dameng, kingbase, highgo, vastbase, goldendb, databend, gaussdb, kwdb, yashandb, databricks, saphana, teradata, vertica, firebird, exasol, opengauss, oceanbase-oracle, questdb, gbase, h2, snowflake, trino, prestosql, hive, spark, db2, informix, influxdb, iris, neo4j, cassandra, bigquery, kylin, sundb, oscar, tdengine, iotdb, xugu, zookeeper, jdbc, access, mq";
const FILE_CAPABLE_CONNECTION_TYPES = new Set(["sqlite", "duckdb", "access", "h2"]);
interface McpScope {
+1
View File
@@ -49,6 +49,7 @@ export const BRIDGE_REQUIRED_TYPES = [
"trino",
"prestosql",
"hive",
"spark",
"db2",
"informix",
"iris",