feat(oscar): add OSCAR database support

This commit is contained in:
t8y2 committed 2026-06-30 20:07:55 +08:00
1 parent d3272b4750
commit 25dcbeb7e4
33 files changed
+204 -19

No files matched your search

+7
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",
+1
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
+28
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 {
@@ -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
+14
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"));
+21 -9
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);
+2
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);
@@ -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,
+10
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