pub mod report;
pub mod unsupported;
use crate::diff::compute_diff;
use crate::dump::generate_dump;
use crate::parser::parse_sql_string;
use crate::pg::connection::PgConnection;
use crate::pg::introspect::introspect_schema;
use crate::util::{sanitize_url, Result, SchemaError};
pub use report::{generate_json_report, generate_text_report, BaselineReport, ObjectCounts};
pub use unsupported::{detect_unsupported_objects, UnsupportedObject};
#[derive(Debug, Clone)]
pub struct BaselineResult {
pub sql_dump: String,
pub report: BaselineReport,
}
pub async fn run_baseline(
connection: &PgConnection,
database_url: &str,
target_schemas: &[String],
output_path: &str,
) -> Result<BaselineResult> {
let introspected = introspect_schema(connection, target_schemas, false).await?;
let header = format!(
"-- Generated by pgmold baseline\n-- Schemas: {}",
target_schemas.join(", ")
);
let sql_dump = generate_dump(&introspected, Some(&header));
let parsed = parse_sql_string(&sql_dump).map_err(|e| {
SchemaError::ValidationError(format!(
"Round-trip failure: generated SQL could not be parsed back: {e}"
))
})?;
let introspected_fingerprint = introspected.fingerprint();
let parsed_fingerprint = parsed.fingerprint();
let round_trip_ok = introspected_fingerprint == parsed_fingerprint;
let diff_ops = compute_diff(&introspected, &parsed);
let zero_diff_ok = diff_ops.is_empty();
let warnings = detect_unsupported_objects(connection, target_schemas).await?;
let report = BaselineReport {
database_url: sanitize_url(database_url),
target_schemas: target_schemas.to_vec(),
output_path: output_path.to_string(),
object_counts: ObjectCounts::from_schema(&introspected),
round_trip_ok,
zero_diff_ok,
fingerprint: introspected_fingerprint,
warnings,
};
Ok(BaselineResult { sql_dump, report })
}
#[cfg(test)]
mod tests {
use super::*;
use crate::model::Schema;
#[test]
fn baseline_result_structure() {
let report = BaselineReport {
database_url: "postgres://localhost/test".into(),
target_schemas: vec!["public".into()],
output_path: "schema.sql".into(),
object_counts: ObjectCounts::from_schema(&Schema::default()),
round_trip_ok: true,
zero_diff_ok: true,
fingerprint: "abc123".into(),
warnings: vec![],
};
let result = BaselineResult {
sql_dump: "-- test".into(),
report,
};
assert!(result.report.is_success());
assert!(!result.report.has_warnings());
}
#[test]
fn baseline_result_with_warnings() {
let report = BaselineReport {
database_url: "postgres://localhost/test".into(),
target_schemas: vec!["public".into()],
output_path: "schema.sql".into(),
object_counts: ObjectCounts::from_schema(&Schema::default()),
round_trip_ok: true,
zero_diff_ok: true,
fingerprint: "abc123".into(),
warnings: vec![UnsupportedObject::CompositeType {
schema: "public".into(),
name: "address".into(),
}],
};
assert!(report.has_warnings());
assert!(report.is_success());
}
#[test]
fn baseline_result_failure() {
let report = BaselineReport {
database_url: "postgres://localhost/test".into(),
target_schemas: vec!["public".into()],
output_path: "schema.sql".into(),
object_counts: ObjectCounts::from_schema(&Schema::default()),
round_trip_ok: false,
zero_diff_ok: false,
fingerprint: "abc123".into(),
warnings: vec![],
};
assert!(!report.is_success());
}
}