feat(oscar): add OSCAR database support
This commit is contained in:
parent
d3272b4750
commit
25dcbeb7e4
|
|
@ -52,8 +52,6 @@ tmp/
|
|||
# Logs
|
||||
*.log
|
||||
|
||||
# Agent JDBC driver JARs
|
||||
agents/*/libs/*.jar
|
||||
|
||||
# Generated changelog data
|
||||
releases-*.json
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
*/libs/*.jar
|
||||
**/libs/*.jar
|
||||
!drivers/oscar/libs/oscarJDBC8.jar
|
||||
gradle.properties
|
||||
build/
|
||||
*/bin/
|
||||
|
|
|
|||
|
|
@ -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.
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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'
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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" },
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ const NAVICAT_STYLE_TABLE_DATA_TYPES = new Set<DatabaseType>([
|
|||
"informix",
|
||||
"bigquery",
|
||||
"sundb",
|
||||
"oscar",
|
||||
"databend",
|
||||
]);
|
||||
|
||||
|
|
|
|||
|
|
@ -282,6 +282,7 @@ const capabilityByType: Partial<Record<DatabaseType, TableStructureCapabilities>
|
|||
starrocks: mysqlCapabilities,
|
||||
goldendb: mysqlCapabilities,
|
||||
sundb: mysqlCapabilities,
|
||||
oscar: unsupportedCapabilities,
|
||||
databend: mysqlCapabilities,
|
||||
gbase: gbaseCapabilities,
|
||||
postgres: postgresCapabilities,
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -518,6 +518,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
|||
bigquery: "BigQuery",
|
||||
kylin: "Kylin",
|
||||
sundb: "SunDB",
|
||||
oscar: "神通 OSCAR",
|
||||
influxdb: "InfluxDB",
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ export type DatabaseType =
|
|||
| "bigquery"
|
||||
| "kylin"
|
||||
| "sundb"
|
||||
| "oscar"
|
||||
| "tdengine"
|
||||
| "xugu"
|
||||
| "iotdb"
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ export const BRIDGE_REQUIRED_TYPES = [
|
|||
"bigquery",
|
||||
"kylin",
|
||||
"sundb",
|
||||
"oscar",
|
||||
"xugu",
|
||||
"jdbc",
|
||||
"access",
|
||||
|
|
|
|||
|
|
@ -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}}
|
||||
|
|
|
|||
Loading…
Reference in New Issue