use anyhow::Result;
use std::path::{Path, PathBuf};
use crate::catalog::Catalog;
use crate::db::connection::connect_with_retry_quiet;
use crate::db::sql_executor::{SqlExecutorConfig, execute_sql_file};
pub async fn import_from_sql_file(
file: PathBuf,
shadow_url: &str,
roles_file: Option<&Path>,
objects: &crate::config::types::Objects,
) -> Result<Catalog> {
tracing::debug!("Executing SQL file: {}", file.display());
let pool = connect_with_retry_quiet(shadow_url).await?;
crate::db::cleaner::clean_shadow_db(&pool, objects).await?;
if let Some(roles_path) = roles_file
&& roles_path.exists()
{
tracing::debug!("Applying roles from: {}", roles_path.display());
crate::schema_ops::apply_roles_file(&pool, roles_path).await?;
}
let executor_config = SqlExecutorConfig {
initialize_session: true,
verbose: false, };
execute_sql_file(&pool, &file, &executor_config).await?;
let catalog = Catalog::load_unfiltered(&pool).await?;
pool.close().await;
Ok(catalog)
}
pub fn validate_sql_file(file_path: &PathBuf) -> Result<()> {
if !file_path.exists() {
return Err(anyhow::anyhow!(
"SQL file does not exist: {}",
file_path.display()
));
}
if !file_path.is_file() {
return Err(anyhow::anyhow!(
"Path is not a file: {}",
file_path.display()
));
}
let content = std::fs::read_to_string(file_path)
.map_err(|e| anyhow::anyhow!("Cannot read SQL file {}: {}", file_path.display(), e))?;
if content.trim().is_empty() {
return Err(anyhow::anyhow!(
"SQL file is empty: {}",
file_path.display()
));
}
let content_lower = content.to_lowercase();
if !content_lower.contains("create")
&& !content_lower.contains("insert")
&& !content_lower.contains("alter")
{
eprintln!(
"⚠️ Warning: SQL file '{}' may not contain valid SQL statements",
file_path.display()
);
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use std::env;
#[test]
fn test_validate_sql_file() {
let temp_dir = env::temp_dir().join("pgmt_test_sql_file_validation");
let _ = std::fs::remove_dir_all(&temp_dir);
std::fs::create_dir_all(&temp_dir).unwrap();
let valid_file = temp_dir.join("valid.sql");
std::fs::write(&valid_file, "CREATE TABLE test (id SERIAL);").unwrap();
assert!(validate_sql_file(&valid_file).is_ok());
let empty_file = temp_dir.join("empty.sql");
std::fs::write(&empty_file, "").unwrap();
assert!(validate_sql_file(&empty_file).is_err());
let missing_file = temp_dir.join("missing.sql");
assert!(validate_sql_file(&missing_file).is_err());
let _ = std::fs::remove_dir_all(&temp_dir);
}
#[test]
fn test_validate_sql_file_with_warning() {
let temp_dir = env::temp_dir().join("pgmt_test_sql_file_warning");
let _ = std::fs::remove_dir_all(&temp_dir);
std::fs::create_dir_all(&temp_dir).unwrap();
let questionable_file = temp_dir.join("questionable.sql");
std::fs::write(&questionable_file, "This is not SQL content").unwrap();
assert!(validate_sql_file(&questionable_file).is_ok());
let _ = std::fs::remove_dir_all(&temp_dir);
}
}