use crate::{ast::ForeignKey, SQLError};
pub trait ReferentialCatalog {
fn session_replication_role_is_replica(&self) -> bool;
fn partition_ancestor_tables(&self, table: &str) -> Result<Vec<String>, SQLError>;
fn try_referrers_to(&self, table: &str) -> Result<Vec<(String, ForeignKey)>, String>;
}
use crate::catalog::errors::dml_storage_error;
pub fn referrers_to_for_actions(
catalog: &dyn ReferentialCatalog,
table: &str,
) -> Result<Vec<(String, ForeignKey)>, SQLError> {
if catalog.session_replication_role_is_replica() {
return Ok(Vec::new());
}
let mut output = Vec::new();
for target in catalog.partition_ancestor_tables(table)? {
let referrers = catalog
.try_referrers_to(&target)
.map_err(|err| dml_storage_error("foreign-key lookup", err))?;
for (declaring_table, foreign_key) in &referrers {
if declares_foreign_key(catalog, &referrers, declaring_table, foreign_key)? {
output.push((declaring_table.clone(), foreign_key.clone()));
}
}
}
Ok(output)
}
fn declares_foreign_key(
catalog: &dyn ReferentialCatalog,
referrers: &[(String, ForeignKey)],
declaring_table: &str,
foreign_key: &ForeignKey,
) -> Result<bool, SQLError> {
let Some(object_id) = foreign_key.object_id else {
return Ok(true);
};
for ancestor in catalog
.partition_ancestor_tables(declaring_table)?
.iter()
.skip(1)
{
if referrers
.iter()
.any(|(table, key)| table == ancestor && key.object_id == Some(object_id))
{
return Ok(false);
}
}
Ok(true)
}