fix(jdbc): render generic table DDL for unmatched external JDBC drivers

Closes #8187
This commit is contained in:
t8y2 committed 2026-09-05 01:23:35 +08:00
1 parent ce36a7f40d
commit 3be523b2f5
4 files changed
+464 -6

No files matched your search

+73 -6
View File
@@ -3166,12 +3166,12 @@ mod tests {
clickhouse_metadata_database, dameng_object_statistics_dba_segments_sql,
dameng_object_statistics_rows_only_sql, dameng_object_statistics_user_segments_sql, deduplicate_column_infos,
ephemeral_agent_metadata_session_id, external_driver_statistics_dialect, external_driver_statistics_query_plan,
external_driver_uses_mysql_ddl, filter_mongodb_agent_collections, filter_mysql_system_databases_for_config,
filter_object_infos, filter_table_infos, filter_visible_schema_names, finalize_object_source,
gaussdb_m_view_object_source_sql, gbase8a_object_statistics_sql, is_agent_postgres_metadata_fallback_config,
is_mysql_external_driver_config, is_oracle_external_driver_config, is_retryable_metadata_error,
metadata_error_action, metadata_name_or_comment_matches, mysql_database_list_timeout,
mysql_external_driver_ddl_from_query_result, mysql_external_driver_ddl_sql,
external_driver_uses_generic_ddl, external_driver_uses_mysql_ddl, filter_mongodb_agent_collections,
filter_mysql_system_databases_for_config, filter_object_infos, filter_table_infos, filter_visible_schema_names,
finalize_object_source, gaussdb_m_view_object_source_sql, gbase8a_object_statistics_sql,
is_agent_postgres_metadata_fallback_config, is_mysql_external_driver_config, is_oracle_external_driver_config,
is_retryable_metadata_error, metadata_error_action, metadata_name_or_comment_matches,
mysql_database_list_timeout, mysql_external_driver_ddl_from_query_result, mysql_external_driver_ddl_sql,
mysql_object_source_ddl_column_index, mysql_object_source_sql, mysql_table_list_source_for_config,
mysql_table_metadata_catalog, normalize_information_schema_table_type, oracle_columns_from_query_result,
oracle_columns_sql, oracle_columns_sql_for_resolved_owner, oracle_current_schema_from_query_result,
@@ -3603,6 +3603,30 @@ mod tests {
assert!(!is_oracle_external_driver_config(&config));
}
#[test]
fn external_driver_generic_ddl_applies_to_unmatched_jdbc_connections() {
// JDBCX-style wrappers match no vendor DDL dialect and fall back to the
// plugin's generic DatabaseMetaData renderer.
let mut config = test_connection_config(DatabaseType::Jdbc);
config.connection_string = Some("jdbcx:wrap-jdbc:jdbc:mysql://127.0.0.1:3306/demo".to_string());
config.jdbc_driver_class = Some("io.github.jdbcx.WrappedDriver".to_string());
assert!(external_driver_uses_generic_ddl(&config));
assert!(!external_driver_uses_mysql_ddl(&config));
assert!(!is_oracle_external_driver_config(&config));
// Vendor dialects keep their dedicated SQL paths.
config.connection_string = Some("jdbc:mysql://127.0.0.1:3306/demo".to_string());
config.jdbc_driver_class = None;
assert!(!external_driver_uses_generic_ddl(&config));
config.connection_string = Some("jdbc:oracle:thin:@//127.0.0.1:1521/ORCL".to_string());
assert!(!external_driver_uses_generic_ddl(&config));
// Vendor-typed database never routes through the generic renderer.
let oracle = test_connection_config(DatabaseType::Oracle);
assert!(!external_driver_uses_generic_ddl(&oracle));
}
#[test]
fn external_driver_statistics_dialect_matches_jdbc_url_vendor() {
let mut config = test_connection_config(DatabaseType::Jdbc);
@@ -7804,6 +7828,11 @@ async fn get_table_ddl_once(
let session = session.clone();
return external_driver_mysql_ddl(session, config.as_ref(), database, schema, table).await;
}
if external_driver_uses_generic_ddl(config.as_ref()) {
let config = config.clone();
let session = session.clone();
return external_driver_jdbc_ddl(session, config.as_ref(), database, schema, table).await;
}
}
#[cfg(feature = "duckdb-sidecar")]
if let Some(client) = extract_pool!(pool_handle.as_ref(), DuckDbWorker) {
@@ -11001,6 +11030,44 @@ async fn external_driver_oracle_ddl(
.ok_or_else(|| "JDBC Oracle plugin returned no table DDL".to_string())
}
/// External JDBC connections that match no vendor DDL dialect (JDBCX wrappers,
/// custom protocol drivers, …) have table DDL rendered by the plugin from
/// plain `DatabaseMetaData` via `getObjectSource`, mirroring the agent-side
/// `DdlBuilder` output.
fn external_driver_uses_generic_ddl(config: &ConnectionConfig) -> bool {
config.db_type == DatabaseType::Jdbc
&& !is_oracle_external_driver_config(config)
&& !external_driver_uses_mysql_ddl(config)
}
async fn external_driver_jdbc_ddl(
session: std::sync::Arc<crate::plugins::PluginDriverSession>,
config: &ConnectionConfig,
database: &str,
schema: &str,
table: &str,
) -> Result<String, String> {
let result: serde_json::Value = session
.invoke_with_timeout(
"getObjectSource",
serde_json::json!({
"connection": config,
"database": database,
"schema": schema,
"name": table,
"object_type": "TABLE",
}),
agent_metadata_timeout(Some(config)),
)
.await?;
result
.get("source")
.and_then(serde_json::Value::as_str)
.filter(|source| !source.trim().is_empty())
.map(str::to_string)
.ok_or_else(|| "JDBC plugin returned no table DDL".to_string())
}
fn normalize_mysql_display_ddl(sql: String) -> String {
ensure_display_ddl_terminated(repair_mysql_ddl_comments(&sql))
}