use super::PartitionCatalog;
use crate::SQLError;
use std::collections::BTreeSet;
pub fn partition_hierarchy_root(
catalog: &dyn PartitionCatalog,
table: &str,
) -> Result<Option<String>, SQLError> {
let mut current = catalog
.try_resolve_table_name(table)
.map_err(|error| SQLError::Internal(format!("resolve table `{table}`: {error}")))?
.ok_or_else(|| SQLError::UnknownTable(table.to_string()))?;
let mut visited = BTreeSet::new();
let mut participates = false;
loop {
if !visited.insert(current.clone()) {
return Err(SQLError::Internal(format!(
"partition hierarchy cycle reaches `{current}`"
)));
}
let hierarchy = catalog
.try_table_hierarchy(¤t)
.map_err(|error| SQLError::Internal(format!("read partition hierarchy: {error}")))?;
participates |= hierarchy.partition_spec.is_some() || hierarchy.is_partition();
if !hierarchy.is_partition() {
return Ok(participates.then_some(current));
}
current = hierarchy
.parents
.first()
.cloned()
.ok_or_else(|| SQLError::Internal("partition has no parent relation".into()))?;
}
}
fn resolve_table(catalog: &dyn PartitionCatalog, table: &str) -> Result<String, SQLError> {
catalog
.try_resolve_table_name(table)
.map_err(|error| SQLError::Internal(format!("resolve table `{table}`: {error}")))?
.ok_or_else(|| SQLError::UnknownTable(table.to_string()))
}
pub fn partition_ancestor_tables(
catalog: &dyn PartitionCatalog,
table: &str,
) -> Result<Vec<String>, SQLError> {
let mut current = resolve_table(catalog, table)?;
let mut output = Vec::new();
let mut visited = BTreeSet::new();
loop {
if !visited.insert(current.clone()) {
return Err(SQLError::Internal(format!(
"partition hierarchy cycle reaches `{current}`"
)));
}
let hierarchy = catalog
.try_table_hierarchy(¤t)
.map_err(|error| SQLError::Internal(format!("read partition hierarchy: {error}")))?;
output.push(current);
if !hierarchy.is_partition() {
return Ok(output);
}
current = hierarchy
.parents
.first()
.cloned()
.ok_or_else(|| SQLError::Internal("partition has no parent relation".into()))?;
}
}
pub fn foreign_key_scan_tables(
catalog: &dyn PartitionCatalog,
table: &str,
) -> Result<Vec<String>, SQLError> {
let mut pending = vec![resolve_table(catalog, table)?];
let mut output = Vec::new();
let mut visited = BTreeSet::new();
while let Some(current) = pending.pop() {
if !visited.insert(current.clone()) {
return Err(SQLError::Internal(format!(
"partition hierarchy cycle reaches `{current}`"
)));
}
let hierarchy = catalog
.try_table_hierarchy(¤t)
.map_err(|error| SQLError::Internal(format!("read partition hierarchy: {error}")))?;
if hierarchy.partition_spec.is_some() {
let mut partitions = catalog.direct_hierarchy_children(¤t)?;
partitions.reverse();
pending.extend(partitions);
}
output.push(current);
}
Ok(output)
}
pub fn partition_identity_owner(
catalog: &dyn PartitionCatalog,
table: &str,
) -> Result<String, SQLError> {
if let Some(root) = partition_hierarchy_root(catalog, table)? {
return Ok(root);
}
catalog
.try_resolve_table_name(table)
.map_err(|error| SQLError::Internal(format!("resolve table `{table}`: {error}")))?
.ok_or_else(|| SQLError::UnknownTable(table.to_string()))
}
#[cfg(test)]
mod tests;