uqa_sql/semantics/
referential.rs1use crate::{ast::ForeignKey, SQLError};
9
10pub trait ReferentialCatalog {
11 fn session_replication_role_is_replica(&self) -> bool;
12 fn partition_ancestor_tables(&self, table: &str) -> Result<Vec<String>, SQLError>;
14 fn try_referrers_to(&self, table: &str) -> Result<Vec<(String, ForeignKey)>, String>;
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.partition_ancestor_tables(table)? {
28 let referrers = catalog
29 .try_referrers_to(&target)
30 .map_err(|err| dml_storage_error("foreign-key lookup", err))?;
31 for (declaring_table, foreign_key) in &referrers {
32 if declares_foreign_key(catalog, &referrers, declaring_table, foreign_key)? {
33 output.push((declaring_table.clone(), foreign_key.clone()));
34 }
35 }
36 }
37 Ok(output)
38}
39
40fn declares_foreign_key(
42 catalog: &dyn ReferentialCatalog,
43 referrers: &[(String, ForeignKey)],
44 declaring_table: &str,
45 foreign_key: &ForeignKey,
46) -> Result<bool, SQLError> {
47 let Some(object_id) = foreign_key.object_id else {
48 return Ok(true);
49 };
50 for ancestor in catalog
51 .partition_ancestor_tables(declaring_table)?
52 .iter()
53 .skip(1)
54 {
55 if referrers
56 .iter()
57 .any(|(table, key)| table == ancestor && key.object_id == Some(object_id))
58 {
59 return Ok(false);
60 }
61 }
62 Ok(true)
63}