use std::collections::HashMap;
use std::rc::Rc;
use anyhow::anyhow;
use petgraph::graph::Graph as BaseGraph;
use petgraph::graph::NodeIndex;
use petgraph::Directed as DirectedGraph;
use crate::psql::dto::*;
use crate::psql::table_metadata::TableMetadata;
use lezeh_common::types::ResultAnyError;
pub type RowGraph = BaseGraph<Rc<PsqlTableRow>, i32, DirectedGraph>;
pub struct RelationFetcher {
table_metadata: Box<dyn TableMetadata>,
}
impl RelationFetcher {
pub fn new(table_metadata: Box<dyn TableMetadata>) -> RelationFetcher {
return RelationFetcher { table_metadata };
}
}
pub struct FetchRowsAsRoseTreeInput<'a> {
pub table_id: &'a PsqlTableIdentity,
pub column_name: &'a str,
pub column_value: &'a str,
}
impl RelationFetcher {
pub fn fetch_as_graphs<'a>(
&mut self,
input: FetchRowsAsRoseTreeInput,
psql_table_by_id: &'a HashMap<PsqlTableIdentity, PsqlTable>,
) -> ResultAnyError<(RowGraph, NodeIndex)> {
let psql_table = psql_table_by_id.get(&input.table_id);
if psql_table.is_none() {
return Err(anyhow!("Table {} not found", input.table_id));
}
let psql_table: &PsqlTable = psql_table.unwrap();
let row: Rc<PsqlTableRow> = Rc::new(self.table_metadata.get_one_row(
psql_table,
input.column_name,
input.column_value,
)?);
let mut row_graph: RowGraph = RowGraph::new();
let node_index = row_graph.add_node(row.clone());
let mut node_index_by_row: HashMap<Rc<PsqlTableRow>, NodeIndex> = Default::default();
node_index_by_row.insert(row.clone(), node_index);
self.fill_referencing_rows(
&mut row_graph,
row.clone(),
&psql_table_by_id,
&mut node_index_by_row,
)?;
self.fill_referenced_rows(
&mut row_graph,
row.clone(),
&psql_table_by_id,
&mut node_index_by_row,
)?;
return Ok((row_graph, node_index));
}
fn fill_referencing_rows(
&mut self,
row_graph: &mut RowGraph,
current_row: Rc<PsqlTableRow>,
psql_table_by_id: &HashMap<PsqlTableIdentity, PsqlTable>,
node_index_by_row: &mut HashMap<Rc<PsqlTableRow>, NodeIndex>,
) -> ResultAnyError<()> {
for (_key, psql_foreign_key) in current_row.table.referencing_fk_by_constraint_name.clone() {
let foreign_table_id = PsqlTableIdentity::new(
psql_foreign_key.foreign_table_schema.clone(),
psql_foreign_key.foreign_table_name.clone(),
);
let foreign_table = psql_table_by_id[&foreign_table_id].clone();
let parents: Vec<Rc<PsqlTableRow>> = self
.fetch_rows(
foreign_table.clone(),
&foreign_table.primary_column.name,
¤t_row.get_id(&psql_foreign_key.column),
)?
.into_iter()
.map(Rc::new)
.collect();
let current_row_node_index = node_index_by_row.get(¤t_row).unwrap().clone();
for parent_row in parents.iter() {
let parent_node_index = node_index_by_row
.entry(parent_row.clone())
.or_insert_with(|| row_graph.add_node(parent_row.clone()));
row_graph.update_edge(current_row_node_index, *parent_node_index, -1);
self.fill_referencing_rows(
row_graph,
parent_row.clone(),
psql_table_by_id,
node_index_by_row,
)?;
}
}
return Ok(());
}
fn fill_referenced_rows(
&mut self,
row_graph: &mut RowGraph,
current_row: Rc<PsqlTableRow>,
psql_table_by_id: &HashMap<PsqlTableIdentity, PsqlTable>,
node_index_by_row: &mut HashMap<Rc<PsqlTableRow>, NodeIndex>,
) -> ResultAnyError<()> {
for (_key, psql_foreign_key) in current_row.table.referenced_fk_by_constraint_name.clone() {
let foreign_table_id = PsqlTableIdentity::new(
psql_foreign_key.foreign_table_schema.clone(),
psql_foreign_key.foreign_table_name.clone(),
);
let foreign_table = psql_table_by_id[&foreign_table_id].clone();
let children_per_fk: Vec<Rc<PsqlTableRow>> = self
.fetch_rows(
foreign_table.clone(),
&psql_foreign_key.column.name,
¤t_row.get_id(¤t_row.table.primary_column),
)?
.into_iter()
.map(Rc::new)
.collect();
let current_row_node_index = node_index_by_row.get(¤t_row).unwrap().clone();
for child_row in children_per_fk.iter() {
let child_node_index = node_index_by_row
.entry(child_row.clone())
.or_insert_with(|| row_graph.add_node(child_row.clone()));
row_graph.update_edge(*child_node_index, current_row_node_index, -1);
self.fill_referencing_rows(
row_graph,
child_row.clone(),
psql_table_by_id,
node_index_by_row,
)?;
self.fill_referenced_rows(
row_graph,
child_row.clone(),
psql_table_by_id,
node_index_by_row,
)?;
}
}
return Ok(());
}
fn fetch_rows<'a>(
&mut self,
table: PsqlTable,
column_name: &str,
id: &PsqlParamValue,
) -> ResultAnyError<Vec<PsqlTableRow>> {
let rows = self
.table_metadata
.get_rows(table.clone(), column_name, id)?;
return Ok(rows);
}
}