uqa_sql/semantics/
referential.rs1use crate::{ast::ForeignKey, SQLError};
9
10pub trait ReferentialCatalog {
11 fn session_replication_role_is_replica(&self) -> bool;
12 fn hierarchy_ancestor_tables(&self, table: &str) -> Result<Vec<String>, SQLError>;
13 fn try_referrers_to(&self, table: &str) -> Result<Vec<(String, ForeignKey)>, String>;
14 fn partition_hierarchy_root(&self, table: &str) -> Result<Option<String>, SQLError>;
15}
16use crate::catalog::errors::dml_storage_error;
17
18pub fn referrers_to_for_actions(
19 catalog: &dyn ReferentialCatalog,
20 table: &str,
21) -> Result<Vec<(String, ForeignKey)>, SQLError> {
22 if catalog.session_replication_role_is_replica() {
23 return Ok(Vec::new());
24 }
25 let mut output = Vec::new();
26 for target in catalog.hierarchy_ancestor_tables(table)? {
27 let referrers = catalog
28 .try_referrers_to(&target)
29 .map_err(|err| dml_storage_error("foreign-key lookup", err))?;
30 for (declaring_table, foreign_key) in referrers {
31 let referencing_table = catalog
32 .partition_hierarchy_root(&declaring_table)?
33 .unwrap_or(declaring_table);
34 if output.iter().any(|(existing_table, existing_key)| {
35 existing_table == &referencing_table
36 && foreign_keys_equivalent(existing_key, &foreign_key)
37 }) {
38 continue;
39 }
40 output.push((referencing_table, foreign_key));
41 }
42 }
43 Ok(output)
44}
45
46fn foreign_keys_equivalent(left: &ForeignKey, right: &ForeignKey) -> bool {
47 left.name == right.name
48 && left.local_columns == right.local_columns
49 && left.ref_table == right.ref_table
50 && left.ref_columns == right.ref_columns
51 && left.on_update == right.on_update
52 && left.on_delete == right.on_delete
53 && left.on_delete_set_columns == right.on_delete_set_columns
54 && left.match_type == right.match_type
55 && left.enforced == right.enforced
56}