feat(oscar): add OSCAR database support

This commit is contained in:
t8y2 2026-06-30 20:03:56 +08:00
parent d3272b4750
commit 25dcbeb7e4
33 changed files with 204 additions and 19 deletions

2
.gitignore vendored
View File

@ -52,8 +52,6 @@ tmp/
# Logs
*.log
# Agent JDBC driver JARs
agents/*/libs/*.jar
# Generated changelog data
releases-*.json

3
agents/.gitignore vendored
View File

@ -1,4 +1,5 @@
*/libs/*.jar
**/libs/*.jar
!drivers/oscar/libs/oscarJDBC8.jar
gradle.properties
build/
*/bin/

View File

@ -0,0 +1,12 @@
dependencies {
implementation files('libs/oscarJDBC8.jar')
}
tasks.named('shadowJar') {
manifest {
attributes(
'Agent-Label': '\u795e\u901a OSCAR',
'Main-Class': 'com.dbx.agent.oscar.OscarAgent'
)
}
}

Binary file not shown.

View File

@ -0,0 +1,21 @@
package com.dbx.agent.oscar;
import com.dbx.agent.ConfiguredJdbcAgent;
import com.dbx.agent.JdbcAgentProfile;
import com.dbx.agent.JsonRpcServer;
public final class OscarAgent extends ConfiguredJdbcAgent {
public static final JdbcAgentProfile OSCAR_PROFILE = new JdbcAgentProfile(
"com.oscar.Driver",
"jdbc:oscar://{host}:{port}/{database}",
2003
);
public OscarAgent() {
super(OSCAR_PROFILE);
}
public static void main(String[] args) {
new JsonRpcServer(new OscarAgent()).run();
}
}

View File

@ -0,0 +1,16 @@
package com.dbx.agent.oscar;
import com.dbx.agent.JdbcAgentProfile;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
class OscarAgentTest {
@Test
void declaresOscarJdbcProfile() {
JdbcAgentProfile profile = OscarAgent.OSCAR_PROFILE;
Assertions.assertEquals("com.oscar.Driver", profile.getDriverClass());
Assertions.assertEquals("jdbc:oscar://{host}:{port}/{database}", profile.getUrlTemplate());
Assertions.assertEquals(2003, profile.getDefaultPort());
}
}

View File

@ -5,7 +5,7 @@ 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',
'db2', 'informix', 'neo4j', 'cassandra', 'mongodb', 'highgo', 'tdengine', 'yashandb',
'db2', 'informix', 'neo4j', 'cassandra', 'mongodb', 'highgo', 'tdengine', 'yashandb', 'oscar',
'iris', 'iotdb', 'etcd', 'zookeeper', 'kafka'
]

View File

@ -30,6 +30,7 @@
"mongodb": "0.1.24",
"highgo": "0.1.23",
"yashandb": "0.1.21",
"oscar": "0.1.0",
"xugu": "0.1.16",
"iris": "0.1.16",
"iotdb": "0.1.13",

View File

@ -570,6 +570,7 @@ const driverProfiles: Record<
},
kylin: { type: "kylin", port: 7070, user: "ADMIN", label: "Apache Kylin", icon: "kylin" },
sundb: { type: "sundb", port: 22000, user: "root", label: "SunDB", icon: "sundb" },
oscar: { type: "oscar", port: 2003, user: "SYSDBA", label: "神通 OSCAR", icon: "oscar" },
jdbc: { type: "jdbc", port: 0, user: "", label: "JDBC", icon: "jdbc" },
tdengine: { type: "tdengine", port: 6041, user: "root", label: "TDengine", icon: "tdengine" },
xugu: { type: "xugu", port: 5138, user: "", label: "虚谷 XuguDB", icon: "xugu" },
@ -1360,6 +1361,7 @@ const iconTypeMap: Record<string, string> = {
bigquery: "bigquery",
kylin: "kylin",
sundb: "sundb",
oscar: "oscar",
influxdb: "influxdb",
jdbc: "jdbc",
custom_mysql: "mysql",
@ -1426,6 +1428,7 @@ const dbOptions: DbOption[] = [
{ value: "bigquery", label: "BigQuery" },
{ value: "kylin", label: "Kylin" },
{ value: "sundb", label: "SunDB" },
{ value: "oscar", label: "神通 OSCAR" },
{ value: "xugu", label: "虚谷 XuguDB" },
{ value: "iotdb", label: "Apache IoTDB" },
{ value: "etcd", label: "etcd" },

View File

@ -92,7 +92,9 @@ const assetIcons: Record<string, string> = {
zookeeper: "zookeeper",
};
const letterIcons: Record<string, { letter: string; color: string }> = {};
const letterIcons: Record<string, { letter: string; color: string }> = {
oscar: { letter: "O", color: "#2563eb" },
};
const normalizedType = computed(() => props.dbType.toLowerCase().replace(/[\s-]+/g, "_"));
const assetName = computed(() => assetIcons[normalizedType.value]);

View File

@ -18,6 +18,10 @@ describe("connectionAttemptTimeout", () => {
expect(connectionAttemptTimeoutMs({ db_type: "prestosql", connect_timeout_secs: 5, transport_layers: [] })).toBe(AGENT_DRIVER_MIN_CONNECT_TIMEOUT_SECS * 1000 + CONNECTION_ATTEMPT_TIMEOUT_BUFFER_MS);
});
it("uses the startup floor for generic JDBC plugin connections", () => {
expect(connectionAttemptTimeoutMs({ db_type: "jdbc", connect_timeout_secs: 5, transport_layers: [] })).toBe(AGENT_DRIVER_MIN_CONNECT_TIMEOUT_SECS * 1000 + CONNECTION_ATTEMPT_TIMEOUT_BUFFER_MS);
});
it("uses the startup floor for ZooKeeper agent connections", () => {
expect(connectionAttemptTimeoutMs({ db_type: "zookeeper", connect_timeout_secs: 5, transport_layers: [] })).toBe(AGENT_DRIVER_MIN_CONNECT_TIMEOUT_SECS * 1000 + CONNECTION_ATTEMPT_TIMEOUT_BUFFER_MS);
});

View File

@ -27,6 +27,7 @@ const DRIVER_STARTUP_FLOOR_TYPES = new Set<DatabaseType>([
"snowflake",
"trino",
"prestosql",
"jdbc",
"hive",
"db2",
"informix",
@ -35,6 +36,7 @@ const DRIVER_STARTUP_FLOOR_TYPES = new Set<DatabaseType>([
"bigquery",
"kylin",
"sundb",
"oscar",
"tdengine",
"xugu",
"iotdb",

View File

@ -142,6 +142,9 @@ export function connectionUrlPlaceholder(dbType: DatabaseType): string {
case "tdengine":
return "tdengine://user:password@host:6041/database";
case "oscar":
return "oscar://user:password@host:2003/database";
case "xugu":
return "xugu://user:password@host:5138/database";

View File

@ -58,6 +58,7 @@ const SCHEME_PROFILES: Record<string, ConnectionProfile> = {
questdb: { type: "questdb", profile: "questdb", label: "QuestDB", defaultPort: 8812 },
tdengine: { type: "tdengine", profile: "tdengine", label: "TDengine", defaultPort: 6041 },
"taos-ws": { type: "tdengine", profile: "tdengine", label: "TDengine", defaultPort: 6041 },
oscar: { type: "oscar", profile: "oscar", label: "神通 OSCAR", defaultPort: 2003 },
xugu: { type: "xugu", profile: "xugu", label: "XuguDB", defaultPort: 5138 },
iotdb: { type: "iotdb", profile: "iotdb", label: "Apache IoTDB", defaultPort: 6667 },
iris: { type: "iris", profile: "iris", label: "IRIS", defaultPort: 1972 },

View File

@ -30,6 +30,7 @@ export const SCHEMA_AWARE_TYPES = new Set<DatabaseType>([
"db2",
"informix",
"xugu",
"oscar",
"iotdb",
"iris",
"duckdb",
@ -64,6 +65,7 @@ export const TREE_SCHEMA_TYPES = new Set<DatabaseType>([
"h2",
"informix",
"xugu",
"oscar",
"iotdb",
"iris",
"duckdb",

View File

@ -83,6 +83,7 @@ const NAVICAT_STYLE_TABLE_DATA_TYPES = new Set<DatabaseType>([
"informix",
"bigquery",
"sundb",
"oscar",
"databend",
]);

View File

@ -282,6 +282,7 @@ const capabilityByType: Partial<Record<DatabaseType, TableStructureCapabilities>
starrocks: mysqlCapabilities,
goldendb: mysqlCapabilities,
sundb: mysqlCapabilities,
oscar: unsupportedCapabilities,
databend: mysqlCapabilities,
gbase: gbaseCapabilities,
postgres: postgresCapabilities,

View File

@ -276,6 +276,7 @@ const DATA_TYPE_OPTION_ALIASES: Partial<Record<DatabaseType, string>> = {
starrocks: "mysql",
goldendb: "mysql",
sundb: "mysql",
oscar: "oracle",
gbase: "mysql",
gaussdb: "postgres",
kwdb: "postgres",

View File

@ -518,6 +518,7 @@ export const useConnectionStore = defineStore("connection", () => {
bigquery: "BigQuery",
kylin: "Kylin",
sundb: "SunDB",
oscar: "神通 OSCAR",
influxdb: "InfluxDB",
};

View File

@ -51,6 +51,7 @@ export type DatabaseType =
| "bigquery"
| "kylin"
| "sundb"
| "oscar"
| "tdengine"
| "xugu"
| "iotdb"

View File

@ -1517,6 +1517,36 @@
"driverManagement": true
}
},
{
"dbType": "oscar",
"label": "神通 OSCAR",
"runtimeMode": "agent",
"mcpMode": "bridge",
"agentKey": "oscar",
"singleConnectionPool": true,
"metadataConnectionScoped": false,
"skipTcpProbe": true,
"defaultPort": 2003,
"supportLevel": "browse",
"capabilities": {
"queryExecution": true,
"metadataBrowse": true,
"objectBrowser": true,
"objectSource": false,
"schemaSearch": true,
"diagram": false,
"tableDataEdit": true,
"tableStructureEdit": false,
"tableImport": false,
"dataTransfer": false,
"sqlFileExecution": true,
"databaseCreate": false,
"fieldLineage": false,
"sqlExplain": false,
"userAdmin": false,
"driverManagement": true
}
},
{
"dbType": "tdengine",
"label": "TDengine",

View File

@ -223,6 +223,13 @@ const AGENT_CATALOG: &[AgentCatalogEntry] = &[
store_visible: true,
profiles: &[],
},
AgentCatalogEntry {
db_type: DatabaseType::Oscar,
key: "oscar",
label: "神通 OSCAR",
store_visible: true,
profiles: &[],
},
AgentCatalogEntry {
db_type: DatabaseType::Yashandb,
key: "yashandb",

View File

@ -125,6 +125,7 @@ macro_rules! agent_connection_pool_database_type {
| DatabaseType::Bigquery
| DatabaseType::Kylin
| DatabaseType::Sundb
| DatabaseType::Oscar
| DatabaseType::Tdengine
| DatabaseType::Xugu
| DatabaseType::Iotdb

View File

@ -1881,6 +1881,7 @@ fn uses_keyless_row_predicate(database_type: Option<DatabaseType>) -> bool {
| DatabaseType::Vastbase
| DatabaseType::Goldendb
| DatabaseType::Yashandb
| DatabaseType::Oscar
| DatabaseType::Databricks
| DatabaseType::SapHana
| DatabaseType::Teradata
@ -2548,6 +2549,33 @@ mod tests {
);
}
#[test]
fn prepares_oscar_keyless_save_statements_with_schema_qualified_row_predicate() {
let result = prepare_data_grid_save(DataGridSaveStatementOptions {
database_type: Some(DatabaseType::Oscar),
table_meta: DataGridTableMeta {
schema: Some("SYSDBA".to_string()),
table_name: "PEOPLE".to_string(),
primary_keys: vec![],
columns: Some(vec![column("ID", "INTEGER", true, None), column("NAME", "VARCHAR", true, None)]),
},
columns: vec!["ID".to_string(), "NAME".to_string()],
source_columns: None,
rows: vec![vec![json!(1), json!("Ada")]],
dirty_rows: vec![(0, vec![(1, json!("Linus"))])],
deleted_rows: vec![0],
new_rows: vec![],
});
assert_eq!(
result.statements,
vec![
"UPDATE \"SYSDBA\".\"PEOPLE\" SET \"NAME\" = 'Linus' WHERE \"ID\" = 1 AND \"NAME\" = 'Ada';",
"DELETE FROM \"SYSDBA\".\"PEOPLE\" WHERE \"ID\" = 1 AND \"NAME\" = 'Ada';",
]
);
}
#[test]
fn skips_expression_only_source_columns() {
let result = prepare_data_grid_save(DataGridSaveStatementOptions {

View File

@ -24,6 +24,7 @@ pub fn is_single_connection_pool(db_type: &DatabaseType) -> bool {
| DatabaseType::Vastbase
| DatabaseType::Goldendb
| DatabaseType::Yashandb
| DatabaseType::Oscar
| DatabaseType::Firebird
| DatabaseType::Iris
| DatabaseType::OceanbaseOracle

View File

@ -312,6 +312,7 @@ pub enum DatabaseType {
Bigquery,
Kylin,
Sundb,
Oscar,
Tdengine,
Xugu,
Iotdb,
@ -642,6 +643,7 @@ impl ConnectionConfig {
DatabaseType::Kingbase | DatabaseType::Vastbase => Some("postgres"),
DatabaseType::Highgo => Some("highgo"),
DatabaseType::Yashandb => Some("yasdb"),
DatabaseType::Oscar => Some("osrdb"),
DatabaseType::Firebird => Some("employee"),
DatabaseType::H2 => Some("test"),
DatabaseType::Informix => Some("sysmaster"),
@ -796,6 +798,7 @@ impl ConnectionConfig {
DatabaseType::Bigquery => format!("bigquery://{host}/{db_part}"),
DatabaseType::Kylin => format!("kylin://{host}:{port}{db_part}"),
DatabaseType::Sundb => format!("sundb://{host}:{port}{db_part}"),
DatabaseType::Oscar => format!("oscar://{host}:{port}{db_part}"),
DatabaseType::Tdengine => format!("tdengine://{host}:{port}{db_part}"),
DatabaseType::Xugu => format!("xugu://{host}:{port}{db_part}"),
DatabaseType::Iotdb => {
@ -998,6 +1001,9 @@ impl ConnectionConfig {
DatabaseType::Sundb => {
format!("sundb://{}:{}@{host}:{port}{db_part}", username, password)
}
DatabaseType::Oscar => {
format!("oscar://{}:{}@{host}:{port}{db_part}", username, password)
}
DatabaseType::Tdengine => {
format!("tdengine://{}:{}@{host}:{port}{db_part}", username, password)
}
@ -2171,6 +2177,14 @@ mod tests {
assert_eq!(config.connection_url(), "yashandb://sys:secret@10.1.2.3:2883/yasdb");
}
#[test]
fn oscar_url_defaults_to_osrdb_database() {
let mut config = mysql_config("SYSDBA", "secret", None);
config.db_type = DatabaseType::Oscar;
assert_eq!(config.connection_url(), "oscar://SYSDBA:secret@10.1.2.3:2883/osrdb");
}
#[test]
fn mongodb_form_url_without_params_defaults_auth_source_to_admin() {
let config = mongodb_config("root", "secret", Some("admin"));

View File

@ -535,7 +535,11 @@ async fn list_databases_once(state: &AppState, connection_id: &str) -> Result<Ve
let session = session.clone();
drop(connections);
return session
.invoke::<Vec<db::DatabaseInfo>>("listDatabases", serde_json::json!({ "connection": config.as_ref() }))
.invoke_with_timeout::<Vec<db::DatabaseInfo>>(
"listDatabases",
serde_json::json!({ "connection": config.as_ref() }),
agent_metadata_timeout(Some(config.as_ref())),
)
.await;
}
if let Some(client) = extract_pool!(&connections, connection_id, ClickHouse) {
@ -644,9 +648,10 @@ pub async fn list_data_types_core(
let session = session.clone();
drop(connections);
return session
.invoke::<Vec<String>>(
.invoke_with_timeout::<Vec<String>>(
"listDataTypes",
serde_json::json!({ "connection": config.as_ref(), "database": database }),
agent_metadata_timeout(Some(config.as_ref())),
)
.await
.map(deduplicate_data_type_names);
@ -697,9 +702,10 @@ async fn list_schemas_once(
let session = session.clone();
drop(connections);
return session
.invoke::<Vec<String>>(
.invoke_with_timeout::<Vec<String>>(
"listSchemas",
serde_json::json!({ "connection": config.as_ref(), "database": database }),
agent_metadata_timeout(Some(config.as_ref())),
)
.await;
}
@ -1117,9 +1123,10 @@ async fn list_tables_once(
.map(|tables| filter_table_infos(tables, filter, limit, offset, object_types));
}
return session
.invoke::<Vec<db::TableInfo>>(
.invoke_with_timeout::<Vec<db::TableInfo>>(
"listTables",
serde_json::json!({ "connection": config.as_ref(), "database": database, "schema": schema }),
agent_metadata_timeout(Some(config.as_ref())),
)
.await
.map(|tables| filter_table_infos(tables, filter, limit, offset, object_types));
@ -1376,7 +1383,7 @@ async fn external_driver_presto_like_tables(
schema: &str,
) -> Result<Vec<db::TableInfo>, String> {
let result: db::QueryResult = session
.invoke(
.invoke_with_timeout(
"executeQuery",
serde_json::json!({
"connection": config,
@ -1387,6 +1394,7 @@ async fn external_driver_presto_like_tables(
"fetchSize": 1000,
"timeoutSecs": 60
}),
agent_metadata_timeout(Some(config)),
)
.await?;
Ok(presto_like_tables_from_query_result(&result))
@ -2417,9 +2425,10 @@ async fn list_objects_once(
return external_driver_presto_like_objects(session, config.as_ref(), database, schema).await;
}
return session
.invoke::<Vec<db::ObjectInfo>>(
.invoke_with_timeout::<Vec<db::ObjectInfo>>(
"listObjects",
serde_json::json!({ "connection": config.as_ref(), "database": database, "schema": schema }),
agent_metadata_timeout(Some(config.as_ref())),
)
.await;
}
@ -2528,9 +2537,10 @@ async fn list_completion_objects_once(
let session = session.clone();
drop(connections);
return session
.invoke::<Vec<db::ObjectInfo>>(
.invoke_with_timeout::<Vec<db::ObjectInfo>>(
"listObjects",
serde_json::json!({ "connection": config.as_ref(), "database": database, "schema": schema }),
agent_metadata_timeout(Some(config.as_ref())),
)
.await
.map(filter_completion_objects);
@ -2703,7 +2713,7 @@ pub async fn get_columns_core(
let session = session.clone();
drop(connections);
let columns = session
.invoke::<Vec<db::ColumnInfo>>(
.invoke_with_timeout::<Vec<db::ColumnInfo>>(
"getColumns",
serde_json::json!({
"connection": config.as_ref(),
@ -2711,6 +2721,7 @@ pub async fn get_columns_core(
"schema": schema,
"table": table,
}),
agent_metadata_timeout(Some(config.as_ref())),
)
.await?;
return Ok(deduplicate_column_infos(columns));
@ -3558,7 +3569,7 @@ async fn get_object_source_once(
let session = session.clone();
drop(connections);
let result: db::ObjectSource = session
.invoke(
.invoke_with_timeout(
"getObjectSource",
serde_json::json!({
"connection": config.as_ref(),
@ -3567,6 +3578,7 @@ async fn get_object_source_once(
"name": name,
"object_type": &object_type,
}),
agent_metadata_timeout(Some(config.as_ref())),
)
.await?;
return Ok(result);

View File

@ -174,6 +174,7 @@ impl SqlDialectProfile {
| DatabaseType::Dameng
| DatabaseType::Gaussdb
| DatabaseType::Yashandb
| DatabaseType::Oscar
| DatabaseType::OceanbaseOracle
)
}
@ -2520,6 +2521,7 @@ DELIMITER ;";
DatabaseType::Dameng,
DatabaseType::Gaussdb,
DatabaseType::Yashandb,
DatabaseType::Oscar,
DatabaseType::OceanbaseOracle,
] {
let profile = SqlDialectProfile::for_database_type(db_type);

View File

@ -35,6 +35,7 @@ pub fn is_schema_aware(database_type: DatabaseType) -> bool {
| DatabaseType::Highgo
| DatabaseType::Vastbase
| DatabaseType::Yashandb
| DatabaseType::Oscar
| DatabaseType::Databricks
| DatabaseType::SapHana
| DatabaseType::Teradata
@ -72,6 +73,12 @@ pub fn pagination_strategy(database_type: Option<DatabaseType>, context: Paginat
TablePaginationStrategy::FetchFirst
}
Some(DatabaseType::Oracle) => TablePaginationStrategy::Unbounded,
Some(DatabaseType::Oscar)
if matches!(context, PaginationContext::TablePreview | PaginationContext::BoundedRead) =>
{
TablePaginationStrategy::Rownum
}
Some(DatabaseType::Oscar) => TablePaginationStrategy::Unbounded,
Some(DatabaseType::Dameng) => TablePaginationStrategy::FetchFirst,
Some(DatabaseType::Db2) => TablePaginationStrategy::Db2FetchFirst,
Some(DatabaseType::SqlServer) => TablePaginationStrategy::SqlServerTop,

View File

@ -37,6 +37,7 @@ fn qualifies_schema_only_for_schema_aware_databases() {
qualified_table_name(Some(DatabaseType::Xugu), Some("DBX_TEST"), "PRODUCTS"),
"\"DBX_TEST\".\"PRODUCTS\""
);
assert_eq!(qualified_table_name(Some(DatabaseType::Oscar), Some("SYSDBA"), "EMPLOYEE"), "\"SYSDBA\".\"EMPLOYEE\"");
assert_eq!(qualified_table_name(Some(DatabaseType::Jdbc), Some("cbsdw_dwd"), "dwd_test_df"), "dwd_test_df");
assert_eq!(qualified_table_name(Some(DatabaseType::Iotdb), Some("root.test"), "device2"), "root.test.device2");
assert_eq!(
@ -64,10 +65,19 @@ fn maps_table_pagination_strategy_by_database_type() {
assert_eq!(table_pagination_strategy(Some(DatabaseType::OceanbaseOracle)), TablePaginationStrategy::Rownum);
assert_eq!(table_pagination_strategy(Some(DatabaseType::Questdb)), TablePaginationStrategy::QuestDbLimit);
assert_eq!(table_pagination_strategy(Some(DatabaseType::Oracle)), TablePaginationStrategy::Rownum);
assert_eq!(table_pagination_strategy(Some(DatabaseType::Oscar)), TablePaginationStrategy::Rownum);
assert_eq!(
pagination_strategy(Some(DatabaseType::Oracle), PaginationContext::BoundedRead),
TablePaginationStrategy::FetchFirst
);
assert_eq!(
pagination_strategy(Some(DatabaseType::Oscar), PaginationContext::BoundedRead),
TablePaginationStrategy::Rownum
);
assert_eq!(
pagination_strategy(Some(DatabaseType::Oscar), PaginationContext::UserQuery),
TablePaginationStrategy::Unbounded
);
assert_eq!(
pagination_strategy(Some(DatabaseType::Oracle), PaginationContext::UserQuery),
TablePaginationStrategy::Unbounded

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, 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, 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 {
@ -185,10 +185,7 @@ export function createDbxMcpServer(backend: Backend, options: { isWebMode?: bool
if (error) return error;
const scopedConfig = config!;
if (scopedConfig.db_type === "redis") {
return toolError(
"REDIS_COMMAND_REQUIRED",
"Redis connections do not accept SQL through dbx_execute_query. Use dbx_execute_redis_command with a Redis command such as GET key or INFO.",
);
return toolError("REDIS_COMMAND_REQUIRED", "Redis connections do not accept SQL through dbx_execute_query. Use dbx_execute_redis_command with a Redis command such as GET key or INFO.");
}
if (scopedConfig.db_type !== "mongodb") {
const safety = evaluateSqlSafety(sql, { ...sqlSafetyFromEnv(), allowMultipleStatements: true });
@ -288,6 +285,7 @@ export function createDbxMcpServer(backend: Backend, options: { isWebMode?: bool
kwdb: 26257,
rqlite: 4001,
tdengine: 6041,
oscar: 2003,
iotdb: 6667,
xugu: 5138,
};

View File

@ -57,6 +57,7 @@ export const BRIDGE_REQUIRED_TYPES = [
"bigquery",
"kylin",
"sundb",
"oscar",
"xugu",
"jdbc",
"access",

View File

@ -63,6 +63,7 @@ ${StrLoc}
!define UNINSTALLERSIGNCOMMAND "{{uninstaller_sign_cmd}}"
!define ESTIMATEDSIZE "{{estimated_size}}"
!define STARTMENUFOLDER "{{start_menu_folder}}"
!searchreplace WEBVIEW2LOADERSRCPATH "${MAINBINARYSRCPATH}" "\${MAINBINARYNAME}.exe" "\WebView2Loader.dll"
Var PassiveMode
Var UpdateMode
@ -643,6 +644,7 @@ Section Install
; Copy main executable
File "${MAINBINARYSRCPATH}"
File /a "/oname=WebView2Loader.dll" "${WEBVIEW2LOADERSRCPATH}"
; Copy resources
{{#each resources_dirs}}
@ -781,6 +783,7 @@ Section Uninstall
; Delete the app directory and its content from disk
; Copy main executable
Delete "$INSTDIR\${MAINBINARYNAME}.exe"
Delete "$INSTDIR\WebView2Loader.dll"
; Delete resources
{{#each resources}}