use std::collections::HashSet;
use crate::diff::{compute_diff_with_flags, planner::plan_migration_checked, MigrationOp};
use crate::filter::{filter_by_target_schemas, filter_schema, Filter};
use crate::model::Schema;
use crate::pg::connection::PgConnection;
use crate::pg::introspect::introspect_schema;
use crate::provider::load_schema_from_sources;
use crate::util::{Result, SchemaError};
#[derive(Debug)]
pub struct MigrationPlan {
pub ops: Vec<MigrationOp>,
pub current_schema: Schema,
pub target_schema: Schema,
}
#[derive(Debug, Default)]
pub struct PlanOptions {
pub manage_ownership: bool,
pub manage_grants: bool,
pub excluded_grant_roles: HashSet<String>,
pub include_extension_objects: bool,
pub exclude_unmanaged_partitions: bool,
}
pub async fn compute_migration_plan(
schema_sources: &[String],
connection: &PgConnection,
target_schemas: &[String],
filter: &Filter,
options: &PlanOptions,
) -> Result<MigrationPlan> {
let raw_target = load_schema_from_sources(schema_sources)?;
let target_schema = filter_schema(
&filter_by_target_schemas(&raw_target, target_schemas),
filter,
);
let raw_current = introspect_schema(
connection,
target_schemas,
options.include_extension_objects,
)
.await?;
let current_schema = filter_schema(&raw_current, filter);
let current_schema = if options.exclude_unmanaged_partitions {
crate::filter::exclude_unmanaged_partitions(¤t_schema, &target_schema)
} else {
current_schema
};
let ops = plan_migration_checked(compute_diff_with_flags(
¤t_schema,
&target_schema,
options.manage_ownership,
options.manage_grants,
&options.excluded_grant_roles,
))
.map_err(|e| SchemaError::ValidationError(e.to_string()))?;
Ok(MigrationPlan {
ops,
current_schema,
target_schema,
})
}
#[cfg(test)]
mod tests {
use crate::diff::MigrationOp;
use super::*;
#[test]
fn migration_plan_exposes_ops_and_schemas() {
let plan = MigrationPlan {
ops: vec![MigrationOp::DropTable("t".to_string())],
current_schema: Schema::default(),
target_schema: Schema::default(),
};
assert_eq!(plan.ops.len(), 1);
assert!(matches!(plan.ops[0], MigrationOp::DropTable(_)));
}
#[test]
fn plan_options_default_disables_ownership_and_grants() {
let options = PlanOptions::default();
assert!(!options.manage_ownership);
assert!(!options.manage_grants);
assert!(options.excluded_grant_roles.is_empty());
assert!(!options.include_extension_objects);
}
}