fix(jdbc): pair oracle ojdbc maven installs with orai18n

Closes #7261
This commit is contained in:
t8y2 committed 2026-08-27 12:19:16 +08:00
1 parent d70a1231b0
commit e6d25b016e
5 files changed
+166 -12

No files matched your search

+96 -10
View File
@@ -212,10 +212,57 @@ pub async fn install_jdbc_driver_from_maven(
let local_repo = plugin_dir.join("maven-cache");
std::fs::create_dir_all(&local_repo).map_err(|err| err.to_string())?;
let mut command = crate::process::new_tokio_command(&resolver);
let mut resolved = resolve_maven_artifacts(&resolver, &coordinate, &local_repo, &repositories, &env).await?;
if let Some(orai18n) = oracle_orai18n_companion(&resolved) {
match resolve_maven_artifacts(&resolver, &orai18n, &local_repo, &repositories, &env).await {
Ok(mut companion) => resolved.artifacts.append(&mut companion.artifacts),
Err(_) => {
// A missing orai18n companion must not block the driver install;
// affected charsets surface a dedicated hint at runtime.
}
}
}
let root = plugins_root.to_path_buf();
tokio::task::spawn_blocking(move || install_jdbc_maven_bundle(&root, &coordinate, &resolved))
.await
.map_err(|err| err.to_string())??;
list_jdbc_drivers(plugins_root)
}
const ORACLE_JDBC_GROUP_ID: &str = "com.oracle.database.jdbc";
const ORACLE_NLS_GROUP_ID: &str = "com.oracle.database.nls";
const ORAI18N_ARTIFACT_ID: &str = "orai18n";
/// Oracle's thin driver jar ships converters for a small default charset set;
/// multibyte charsets such as ZHS16GBK need the orai18n companion jar, otherwise
/// every metadata call that reads dictionary comments fails wholesale. DBeaver
/// ships the same pairing.
fn oracle_orai18n_companion(resolved: &MavenResolveOutput) -> Option<String> {
let ojdbc = resolved
.artifacts
.iter()
.find(|artifact| artifact.group_id == ORACLE_JDBC_GROUP_ID && artifact.artifact_id.starts_with("ojdbc"))?;
if resolved
.artifacts
.iter()
.any(|artifact| artifact.group_id == ORACLE_NLS_GROUP_ID && artifact.artifact_id == ORAI18N_ARTIFACT_ID)
{
return None;
}
Some(format!("{ORACLE_NLS_GROUP_ID}:{ORAI18N_ARTIFACT_ID}:{}", ojdbc.version))
}
async fn resolve_maven_artifacts(
resolver: &Path,
coordinate: &str,
local_repo: &Path,
repositories: &[String],
env: &PluginRuntimeEnv,
) -> Result<MavenResolveOutput, String> {
let mut command = crate::process::new_tokio_command(resolver);
env.apply_to(&mut command);
command.arg("resolve").arg("--coordinate").arg(&coordinate).arg("--local-repo").arg(&local_repo);
for repo in &repositories {
command.arg("resolve").arg("--coordinate").arg(coordinate).arg("--local-repo").arg(local_repo);
for repo in repositories {
command.arg("--repo").arg(repo);
}
let output = command.output().await.map_err(|err| format!("Failed to run JDBC Maven resolver: {err}"))?;
@@ -224,13 +271,7 @@ pub async fn install_jdbc_driver_from_maven(
return Err(if stderr.is_empty() { "JDBC Maven resolver failed".to_string() } else { stderr });
}
let stdout = String::from_utf8_lossy(&output.stdout);
let resolved: MavenResolveOutput =
serde_json::from_str(&stdout).map_err(|err| format!("Failed to parse JDBC Maven resolver output: {err}"))?;
let root = plugins_root.to_path_buf();
tokio::task::spawn_blocking(move || install_jdbc_maven_bundle(&root, &coordinate, &resolved))
.await
.map_err(|err| err.to_string())??;
list_jdbc_drivers(plugins_root)
serde_json::from_str(&stdout).map_err(|err| format!("Failed to parse JDBC Maven resolver output: {err}"))
}
pub async fn install_prestosql_jdbc_driver(plugins_root: &Path) -> Result<Vec<JdbcDriverInfo>, String> {
@@ -1254,6 +1295,51 @@ mod tests {
assert!(!is_safe_bundle_id("../hive"));
}
fn resolved_artifact(group_id: &str, artifact_id: &str, version: &str) -> MavenResolvedArtifact {
MavenResolvedArtifact {
group_id: group_id.to_string(),
artifact_id: artifact_id.to_string(),
version: version.to_string(),
classifier: String::new(),
extension: "jar".to_string(),
file: format!("/tmp/{artifact_id}-{version}.jar"),
}
}
fn resolved_with(artifacts: Vec<MavenResolvedArtifact>) -> MavenResolveOutput {
MavenResolveOutput {
coordinate: String::new(),
scope: "runtime".to_string(),
repositories: vec!["https://repo.maven.apache.org/maven2/".to_string()],
artifacts,
}
}
#[test]
fn oracle_ojdbc_resolves_matching_orai18n_companion() {
let resolved = resolved_with(vec![resolved_artifact("com.oracle.database.jdbc", "ojdbc11", "21.9.0.0")]);
assert_eq!(oracle_orai18n_companion(&resolved).as_deref(), Some("com.oracle.database.nls:orai18n:21.9.0.0"));
}
#[test]
fn oracle_orai18n_companion_skips_existing_nls_artifact() {
let resolved = resolved_with(vec![
resolved_artifact("com.oracle.database.jdbc", "ojdbc11", "21.9.0.0"),
resolved_artifact("com.oracle.database.nls", "orai18n", "21.9.0.0"),
]);
assert_eq!(oracle_orai18n_companion(&resolved), None);
}
#[test]
fn oracle_orai18n_companion_ignores_non_oracle_drivers() {
for coordinate in
[("org.postgresql", "postgresql"), ("com.oracle.database.xml", "xdb6"), ("mysql", "mysql-connector-java")]
{
let resolved = resolved_with(vec![resolved_artifact(coordinate.0, coordinate.1, "8.0.33")]);
assert_eq!(oracle_orai18n_companion(&resolved), None, "{}", coordinate.1);
}
}
#[test]
fn imports_offline_jdbc_plugin_and_rewrites_bundle_paths() {
let root = std::env::temp_dir().join(format!("dbx-offline-jdbc-test-{}", uuid::Uuid::new_v4()));