use crate::catalog::constraint::{Constraint, ConstraintType};
use crate::catalog::target::AttrTarget;
use crate::diff::comment_utils;
use crate::diff::operations::{ConstraintIdentifier, ConstraintOperation, MigrationStep};
pub fn diff(old: Option<&Constraint>, new: Option<&Constraint>) -> Vec<MigrationStep> {
match (old, new) {
(None, Some(n)) => {
let mut steps = vec![MigrationStep::Constraint(ConstraintOperation::Create(
n.clone(),
))];
if let Some(comment_op) =
comment_utils::handle_comment_creation(&n.comment, AttrTarget::object(n.id()))
{
steps.push(MigrationStep::Constraint(ConstraintOperation::Comment(
comment_op,
)));
}
steps
}
(Some(o), None) => {
vec![MigrationStep::Constraint(ConstraintOperation::Drop(
ConstraintIdentifier::from_constraint(o),
))]
}
(Some(o), Some(n)) => {
let mut steps = Vec::new();
if constraints_differ(o, n) {
steps.push(MigrationStep::Constraint(ConstraintOperation::Drop(
ConstraintIdentifier::from_constraint(o),
)));
steps.push(MigrationStep::Constraint(ConstraintOperation::Create(
n.clone(),
)));
} else {
let comment_ops = comment_utils::handle_comment_diff(Some(o), Some(n), || {
AttrTarget::object(n.id())
});
for comment_op in comment_ops {
steps.push(MigrationStep::Constraint(ConstraintOperation::Comment(
comment_op,
)));
}
}
steps
}
(None, None) => vec![],
}
}
fn constraints_differ(old: &Constraint, new: &Constraint) -> bool {
constraint_types_differ(&old.constraint_type, &new.constraint_type)
}
fn constraint_types_differ(old: &ConstraintType, new: &ConstraintType) -> bool {
match (old, new) {
(
ConstraintType::Unique { columns: old_cols },
ConstraintType::Unique { columns: new_cols },
) => old_cols != new_cols,
(
ConstraintType::ForeignKey {
columns: old_cols,
referenced_schema: old_ref_schema,
referenced_table: old_ref_table,
referenced_columns: old_ref_cols,
on_delete: old_on_delete,
on_update: old_on_update,
deferrable: old_deferrable,
initially_deferred: old_initially_deferred,
},
ConstraintType::ForeignKey {
columns: new_cols,
referenced_schema: new_ref_schema,
referenced_table: new_ref_table,
referenced_columns: new_ref_cols,
on_delete: new_on_delete,
on_update: new_on_update,
deferrable: new_deferrable,
initially_deferred: new_initially_deferred,
},
) => {
old_cols != new_cols
|| old_ref_schema != new_ref_schema
|| old_ref_table != new_ref_table
|| old_ref_cols != new_ref_cols
|| old_on_delete != new_on_delete
|| old_on_update != new_on_update
|| old_deferrable != new_deferrable
|| old_initially_deferred != new_initially_deferred
}
(
ConstraintType::Check {
expression: old_expr,
},
ConstraintType::Check {
expression: new_expr,
},
) => old_expr != new_expr,
(
ConstraintType::Exclusion {
elements: old_elements,
operator_classes: old_opcnames,
operators: old_operators,
index_method: old_method,
predicate: old_predicate,
},
ConstraintType::Exclusion {
elements: new_elements,
operator_classes: new_opcnames,
operators: new_operators,
index_method: new_method,
predicate: new_predicate,
},
) => {
old_elements != new_elements
|| old_opcnames != new_opcnames
|| old_operators != new_operators
|| old_method != new_method
|| old_predicate != new_predicate
}
_ => true,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::catalog::id::DbObjectId;
#[test]
fn test_foreign_key_diff() {
let old_fk = Constraint {
schema: "public".to_string(),
table_name: "orders".to_string(),
name: "orders_user_id_fkey".to_string(),
constraint_type: ConstraintType::ForeignKey {
columns: vec!["user_id".to_string()],
referenced_schema: "public".to_string(),
referenced_table: "users".to_string(),
referenced_columns: vec!["id".to_string()],
on_delete: None,
on_update: None,
deferrable: false,
initially_deferred: false,
},
comment: None,
depends_on: vec![
DbObjectId::Table {
schema: "public".to_string(),
name: "orders".to_string(),
},
DbObjectId::Table {
schema: "public".to_string(),
name: "users".to_string(),
},
],
};
let new_fk = Constraint {
schema: "public".to_string(),
table_name: "orders".to_string(),
name: "orders_user_id_fkey".to_string(),
constraint_type: ConstraintType::ForeignKey {
columns: vec!["user_id".to_string()],
referenced_schema: "public".to_string(),
referenced_table: "users".to_string(),
referenced_columns: vec!["id".to_string()],
on_delete: Some("CASCADE".to_string()),
on_update: None,
deferrable: false,
initially_deferred: false,
},
comment: None,
depends_on: vec![
DbObjectId::Table {
schema: "public".to_string(),
name: "orders".to_string(),
},
DbObjectId::Table {
schema: "public".to_string(),
name: "users".to_string(),
},
],
};
let steps = diff(Some(&old_fk), Some(&new_fk));
assert_eq!(steps.len(), 2); }
}