use super::{Schema, TableId};
use std::collections::VecDeque;
#[derive(Debug)]
pub struct SchemaGraph {
pub schema: Schema,
pub parents: Vec<Vec<TableId>>,
pub children: Vec<Vec<TableId>>,
}
#[derive(Debug)]
pub struct TopoSortResult {
pub order: Vec<TableId>,
pub cyclic_tables: Vec<TableId>,
}
impl SchemaGraph {
pub fn from_schema(schema: Schema) -> Self {
let n = schema.table_schemas.len();
let mut parents: Vec<Vec<TableId>> = vec![Vec::new(); n];
let mut children: Vec<Vec<TableId>> = vec![Vec::new(); n];
for table in &schema.table_schemas {
let child_id = table.id;
for fk in &table.foreign_keys {
if let Some(parent_id) = fk.referenced_table_id {
if parent_id != child_id {
if !parents[child_id.0 as usize].contains(&parent_id) {
parents[child_id.0 as usize].push(parent_id);
}
if !children[parent_id.0 as usize].contains(&child_id) {
children[parent_id.0 as usize].push(child_id);
}
}
}
}
}
Self {
schema,
parents,
children,
}
}
pub fn len(&self) -> usize {
self.schema.len()
}
pub fn is_empty(&self) -> bool {
self.schema.is_empty()
}
pub fn table_name(&self, id: TableId) -> Option<&str> {
self.schema.table(id).map(|t| t.name.as_str())
}
pub fn has_self_reference(&self, id: TableId) -> bool {
self.schema
.table(id)
.map(|t| {
t.foreign_keys
.iter()
.any(|fk| fk.referenced_table_id == Some(id))
})
.unwrap_or(false)
}
pub fn self_referential_tables(&self) -> Vec<TableId> {
(0..self.len())
.map(|i| TableId(i as u32))
.filter(|&id| self.has_self_reference(id))
.collect()
}
pub fn topo_sort(&self) -> TopoSortResult {
let n = self.len();
if n == 0 {
return TopoSortResult {
order: Vec::new(),
cyclic_tables: Vec::new(),
};
}
let mut in_degree: Vec<usize> = vec![0; n];
for (i, parents) in self.parents.iter().enumerate() {
in_degree[i] = parents.len();
}
let mut queue: VecDeque<TableId> = VecDeque::new();
for (i, °) in in_degree.iter().enumerate() {
if deg == 0 {
queue.push_back(TableId(i as u32));
}
}
let mut order = Vec::with_capacity(n);
while let Some(table_id) = queue.pop_front() {
order.push(table_id);
for &child_id in &self.children[table_id.0 as usize] {
in_degree[child_id.0 as usize] -= 1;
if in_degree[child_id.0 as usize] == 0 {
queue.push_back(child_id);
}
}
}
let cyclic_tables: Vec<TableId> = in_degree
.iter()
.enumerate()
.filter(|(_, °)| deg > 0)
.map(|(i, _)| TableId(i as u32))
.collect();
TopoSortResult {
order,
cyclic_tables,
}
}
pub fn processing_order(&self) -> (Vec<TableId>, Vec<TableId>) {
let result = self.topo_sort();
(result.order, result.cyclic_tables)
}
pub fn is_ancestor(&self, ancestor: TableId, descendant: TableId) -> bool {
if ancestor == descendant {
return false;
}
let mut visited = vec![false; self.len()];
let mut queue = VecDeque::new();
queue.push_back(descendant);
while let Some(current) = queue.pop_front() {
for &parent in &self.parents[current.0 as usize] {
if parent == ancestor {
return true;
}
if !visited[parent.0 as usize] {
visited[parent.0 as usize] = true;
queue.push_back(parent);
}
}
}
false
}
pub fn ancestors(&self, id: TableId) -> Vec<TableId> {
let mut ancestors = Vec::new();
let mut visited = vec![false; self.len()];
let mut queue = VecDeque::new();
for &parent in &self.parents[id.0 as usize] {
queue.push_back(parent);
visited[parent.0 as usize] = true;
}
while let Some(current) = queue.pop_front() {
ancestors.push(current);
for &parent in &self.parents[current.0 as usize] {
if !visited[parent.0 as usize] {
visited[parent.0 as usize] = true;
queue.push_back(parent);
}
}
}
ancestors
}
pub fn descendants(&self, id: TableId) -> Vec<TableId> {
let mut descendants = Vec::new();
let mut visited = vec![false; self.len()];
let mut queue = VecDeque::new();
for &child in &self.children[id.0 as usize] {
queue.push_back(child);
visited[child.0 as usize] = true;
}
while let Some(current) = queue.pop_front() {
descendants.push(current);
for &child in &self.children[current.0 as usize] {
if !visited[child.0 as usize] {
visited[child.0 as usize] = true;
queue.push_back(child);
}
}
}
descendants
}
pub fn root_tables(&self) -> Vec<TableId> {
self.parents
.iter()
.enumerate()
.filter(|(_, parents)| parents.is_empty())
.map(|(i, _)| TableId(i as u32))
.collect()
}
pub fn leaf_tables(&self) -> Vec<TableId> {
self.children
.iter()
.enumerate()
.filter(|(_, children)| children.is_empty())
.map(|(i, _)| TableId(i as u32))
.collect()
}
}