use crate::catalog::function::{Function, FunctionKind, FunctionParam};
use crate::diff::comment_utils;
use crate::diff::operations::{FunctionIdentifier, FunctionOperation, MigrationStep};
fn same_signature(old: &Function, new: &Function) -> bool {
if old.parameters.len() != new.parameters.len() {
return false;
}
for (o, n) in old.parameters.iter().zip(new.parameters.iter()) {
if o.data_type != n.data_type || o.mode != n.mode {
return false;
}
}
if old.return_type != new.return_type {
return false;
}
true
}
pub fn format_parameter_list(params: &[FunctionParam]) -> String {
let param_strs: Vec<String> = params
.iter()
.map(|p| {
let mode_str = match &p.mode {
Some(mode) => format!("{} ", mode),
None => "".to_string(),
};
let name_str = match &p.name {
Some(name) => format!("{} ", name),
None => "".to_string(),
};
format!("{}{}{}", mode_str, name_str, p.data_type)
})
.collect();
param_strs.join(", ")
}
pub fn format_return_clause(func: &Function) -> String {
match &func.return_type {
Some(rt) => format!(" RETURNS {}", rt),
None => "".to_string(),
}
}
pub fn format_attributes(func: &Function) -> String {
let mut attrs = Vec::new();
attrs.push(format!("LANGUAGE {}", func.language));
if func.kind == FunctionKind::Function {
attrs.push(func.volatility.clone());
}
if func.kind == FunctionKind::Function && func.is_strict {
attrs.push("STRICT".to_string());
}
attrs.push(format!("SECURITY {}", func.security_type));
attrs.join(" ")
}
pub fn diff(old: Option<&Function>, new: Option<&Function>) -> Vec<MigrationStep> {
match (old, new) {
(None, Some(n)) => {
let kind_str = match n.kind {
FunctionKind::Function => "FUNCTION",
FunctionKind::Procedure => "PROCEDURE",
FunctionKind::Aggregate => "AGGREGATE FUNCTION",
};
let params = format_parameter_list(&n.parameters);
let returns = format_return_clause(n);
let attributes = format_attributes(n);
let mut steps = vec![MigrationStep::Function(FunctionOperation::Create {
schema: n.schema.clone(),
name: n.name.clone(),
arguments: n.arguments.clone(),
kind: kind_str.to_string(),
parameters: params,
returns,
attributes,
definition: n.definition.clone(),
})];
if let Some(comment_op) = comment_utils::handle_comment_creation(
&n.comment,
FunctionIdentifier {
schema: n.schema.clone(),
name: n.name.clone(),
arguments: n.arguments.clone(),
},
) {
steps.push(MigrationStep::Function(FunctionOperation::Comment(
comment_op,
)));
}
steps
}
(Some(o), None) => {
let kind_str = match o.kind {
FunctionKind::Function => "FUNCTION",
FunctionKind::Procedure => "PROCEDURE",
FunctionKind::Aggregate => "AGGREGATE FUNCTION",
};
let param_types: Vec<String> =
o.parameters.iter().map(|p| p.data_type.clone()).collect();
vec![MigrationStep::Function(FunctionOperation::Drop {
schema: o.schema.clone(),
name: o.name.clone(),
arguments: o.arguments.clone(),
kind: kind_str.to_string(),
parameter_types: param_types.join(", "),
})]
}
(Some(o), Some(n)) => {
if !same_signature(o, n) {
let mut steps = Vec::new();
steps.extend(diff(Some(o), None)); steps.extend(diff(None, Some(n))); return steps;
}
let o_attributes = format_attributes(o);
let n_attributes = format_attributes(n);
if o.definition != n.definition || o_attributes != n_attributes {
let kind_str = match n.kind {
FunctionKind::Function => "FUNCTION",
FunctionKind::Procedure => "PROCEDURE",
FunctionKind::Aggregate => "AGGREGATE FUNCTION",
};
let params = format_parameter_list(&n.parameters);
let returns = format_return_clause(n);
let attributes = n_attributes;
let mut steps = vec![MigrationStep::Function(FunctionOperation::Replace {
schema: n.schema.clone(),
name: n.name.clone(),
arguments: n.arguments.clone(),
kind: kind_str.to_string(),
parameters: params,
returns,
attributes,
definition: n.definition.clone(),
})];
let comment_ops =
comment_utils::handle_comment_diff(Some(o), Some(n), || FunctionIdentifier {
schema: n.schema.clone(),
name: n.name.clone(),
arguments: n.arguments.clone(),
});
for comment_op in comment_ops {
steps.push(MigrationStep::Function(FunctionOperation::Comment(
comment_op,
)));
}
steps
} else {
let comment_ops =
comment_utils::handle_comment_diff(Some(o), Some(n), || FunctionIdentifier {
schema: n.schema.clone(),
name: n.name.clone(),
arguments: n.arguments.clone(),
});
let mut steps = Vec::new();
for comment_op in comment_ops {
steps.push(MigrationStep::Function(FunctionOperation::Comment(
comment_op,
)));
}
steps
}
}
(None, None) => {
Vec::new() }
}
}