use super::ObjectAddress;
use std::cmp::Ordering;
use std::fmt::Write as _;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SharedDependencyKind {
Owner,
Acl,
InitialAcl,
Policy,
Tablespace,
}
impl SharedDependencyKind {
#[must_use]
pub const fn code(self) -> char {
match self {
Self::Owner => 'o',
Self::Acl => 'a',
Self::InitialAcl => 'i',
Self::Policy => 'r',
Self::Tablespace => 't',
}
}
fn describe(self, object: &str) -> String {
match self {
Self::Owner => format!("owner of {object}"),
Self::Acl => format!("privileges for {object}"),
Self::InitialAcl => format!("initial privileges for {object}"),
Self::Policy => format!("target of {object}"),
Self::Tablespace => format!("tablespace for {object}"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SharedDependency {
pub database: u32,
pub dependent: ObjectAddress,
pub referenced: ObjectAddress,
pub kind: SharedDependencyKind,
}
const MAX_REPORTED_DEPENDENTS: usize = 100;
pub fn shared_dependency_detail<'a>(
dependencies: impl IntoIterator<Item = &'a SharedDependency>,
referenced: ObjectAddress,
describe: super::Describe<'_>,
) -> Result<Option<String>, crate::SQLError> {
let mut dependents = dependencies
.into_iter()
.filter(|dependency| dependency.referenced == referenced)
.map(|dependency| (dependency.dependent, dependency.kind))
.collect::<Vec<_>>();
dependents.sort_by(|(left, left_kind), (right, right_kind)| {
shared_dependency_order(left, right).then(left_kind.code().cmp(&right_kind.code()))
});
let mut lines = Vec::new();
let mut unreported = 0_usize;
for (index, (object, kind)) in dependents.into_iter().enumerate() {
if index >= MAX_REPORTED_DEPENDENTS {
unreported += 1;
continue;
}
if let Some(description) = describe(object)? {
lines.push(kind.describe(&description));
}
}
if lines.is_empty() {
return Ok(None);
}
let mut detail = lines.join("\n");
if unreported > 0 {
write!(
detail,
"\nand {unreported} other object{} (see server log for list)",
if unreported == 1 { "" } else { "s" }
)
.expect("writing to a String cannot fail");
}
Ok(Some(detail))
}
fn shared_dependency_order(left: &ObjectAddress, right: &ObjectAddress) -> Ordering {
left.object_id
.cmp(&right.object_id)
.then(left.class_id.cmp(&right.class_id))
.then(
left.sub_id
.cast_unsigned()
.cmp(&right.sub_id.cast_unsigned()),
)
}
#[cfg(test)]
mod tests;