use anyhow::Result;
use client_core::sql_diff::generate_schema_diff;
use rust_i18n::t;
use std::fs;
use std::path::PathBuf;
use tracing::info;
pub async fn run_diff_sql(
old_sql_path: PathBuf,
new_sql_path: PathBuf,
old_version: Option<String>,
new_version: Option<String>,
output_file: String,
) -> Result<()> {
info!("🔄 Starting SQL file diff comparison...");
info!("📄 Old version SQL: {path}", path = old_sql_path.display());
info!("📄 New version SQL: {path}", path = new_sql_path.display());
if !old_sql_path.exists() {
return Err(anyhow::anyhow!(
t!("diff_sql.old_sql_not_found", path = old_sql_path.display()).to_string()
));
}
if !new_sql_path.exists() {
return Err(anyhow::anyhow!(
t!("diff_sql.new_sql_not_found", path = new_sql_path.display()).to_string()
));
}
info!("📖 Reading SQL files...");
let old_sql_content = fs::read_to_string(&old_sql_path).map_err(|e| {
client_core::error::DuckError::custom(
t!("diff_sql.read_old_failed", error = e.to_string()).to_string(),
)
})?;
let new_sql_content = fs::read_to_string(&new_sql_path).map_err(|e| {
client_core::error::DuckError::custom(
t!("diff_sql.read_new_failed", error = e.to_string()).to_string(),
)
})?;
let from_version = old_version.as_deref().unwrap_or("unknown");
let to_version = new_version.as_deref().unwrap_or("latest");
info!("🔍 Analyzing SQL differences...");
let (diff_sql, description) = generate_schema_diff(
Some(&old_sql_content),
&new_sql_content,
Some(from_version),
to_version,
)
.map_err(|e| {
client_core::error::DuckError::custom(
t!("diff_sql.generate_failed", error = e.to_string()).to_string(),
)
})?;
info!(
"📊 SQL diff analysis result: {description}",
description = description
);
let meaningful_lines: Vec<&str> = diff_sql
.lines()
.filter(|line| !line.trim().is_empty() && !line.trim().starts_with("--"))
.collect();
if meaningful_lines.is_empty() {
info!("✅ No database schema changes, upgrade not needed");
info!("📄 Generated empty diff file: {file}", file = output_file);
let empty_diff_content = format!(
"-- SQL diff analysis result\n-- {description}\n-- No SQL execution needed; database schema unchanged\n"
);
fs::write(&output_file, empty_diff_content).map_err(|e| {
client_core::error::DuckError::custom(
t!("diff_sql.write_failed", error = e.to_string()).to_string(),
)
})?;
} else {
fs::write(&output_file, &diff_sql).map_err(|e| {
client_core::error::DuckError::custom(
t!("diff_sql.write_failed", error = e.to_string()).to_string(),
)
})?;
info!("📄 SQL diff file saved: {file}", file = output_file);
info!(
"📋 Found {count} executable SQL statements",
count = meaningful_lines.len()
);
let diff_lines: Vec<&str> = diff_sql.lines().take(10).collect();
info!("📋 SQL diff preview (first 10 lines):");
for line in diff_lines {
if !line.trim().is_empty() {
info!(" {}", line);
}
}
if diff_sql.lines().count() > 10 {
info!(" ... See more in file: {file}", file = output_file);
}
}
info!("💡 Usage tips:");
info!(" 1. Backup your database first");
info!(" 2. Verify the diff SQL in test environment");
info!(" 3. Execute in production after confirmation");
if !meaningful_lines.is_empty() {
info!(
" 4. Execute example: mysql -u username -p database_name < {file}",
file = output_file
);
}
info!("✅ SQL diff comparison completed");
Ok(())
}