uqa_sql/semantics/partition/
identity.rs1use super::PartitionCatalog;
8use crate::SQLError;
9use std::collections::BTreeSet;
10pub fn partition_hierarchy_root(
11 catalog: &dyn PartitionCatalog,
12 table: &str,
13) -> Result<Option<String>, SQLError> {
14 let mut current = catalog
15 .try_resolve_table_name(table)
16 .map_err(|error| SQLError::Internal(format!("resolve table `{table}`: {error}")))?
17 .ok_or_else(|| SQLError::UnknownTable(table.to_string()))?;
18 let mut visited = BTreeSet::new();
19 let mut participates = false;
20 loop {
21 if !visited.insert(current.clone()) {
22 return Err(SQLError::Internal(format!(
23 "partition hierarchy cycle reaches `{current}`"
24 )));
25 }
26 let hierarchy = catalog
27 .try_table_hierarchy(¤t)
28 .map_err(|error| SQLError::Internal(format!("read partition hierarchy: {error}")))?;
29 participates |= hierarchy.partition_spec.is_some() || hierarchy.is_partition();
30 if !hierarchy.is_partition() {
31 return Ok(participates.then_some(current));
32 }
33 current = hierarchy
34 .parents
35 .first()
36 .cloned()
37 .ok_or_else(|| SQLError::Internal("partition has no parent relation".into()))?;
38 }
39}
40
41fn resolve_table(catalog: &dyn PartitionCatalog, table: &str) -> Result<String, SQLError> {
42 catalog
43 .try_resolve_table_name(table)
44 .map_err(|error| SQLError::Internal(format!("resolve table `{table}`: {error}")))?
45 .ok_or_else(|| SQLError::UnknownTable(table.to_string()))
46}
47
48pub fn partition_ancestor_tables(
50 catalog: &dyn PartitionCatalog,
51 table: &str,
52) -> Result<Vec<String>, SQLError> {
53 let mut current = resolve_table(catalog, table)?;
54 let mut output = Vec::new();
55 let mut visited = BTreeSet::new();
56 loop {
57 if !visited.insert(current.clone()) {
58 return Err(SQLError::Internal(format!(
59 "partition hierarchy cycle reaches `{current}`"
60 )));
61 }
62 let hierarchy = catalog
63 .try_table_hierarchy(¤t)
64 .map_err(|error| SQLError::Internal(format!("read partition hierarchy: {error}")))?;
65 output.push(current);
66 if !hierarchy.is_partition() {
67 return Ok(output);
68 }
69 current = hierarchy
70 .parents
71 .first()
72 .cloned()
73 .ok_or_else(|| SQLError::Internal("partition has no parent relation".into()))?;
74 }
75}
76
77pub fn foreign_key_scan_tables(
79 catalog: &dyn PartitionCatalog,
80 table: &str,
81) -> Result<Vec<String>, SQLError> {
82 let mut pending = vec![resolve_table(catalog, table)?];
83 let mut output = Vec::new();
84 let mut visited = BTreeSet::new();
85 while let Some(current) = pending.pop() {
86 if !visited.insert(current.clone()) {
87 return Err(SQLError::Internal(format!(
88 "partition hierarchy cycle reaches `{current}`"
89 )));
90 }
91 let hierarchy = catalog
92 .try_table_hierarchy(¤t)
93 .map_err(|error| SQLError::Internal(format!("read partition hierarchy: {error}")))?;
94 if hierarchy.partition_spec.is_some() {
95 let mut partitions = catalog.direct_hierarchy_children(¤t)?;
96 partitions.reverse();
97 pending.extend(partitions);
98 }
99 output.push(current);
100 }
101 Ok(output)
102}
103
104pub fn partition_identity_owner(
106 catalog: &dyn PartitionCatalog,
107 table: &str,
108) -> Result<String, SQLError> {
109 if let Some(root) = partition_hierarchy_root(catalog, table)? {
110 return Ok(root);
111 }
112 catalog
113 .try_resolve_table_name(table)
114 .map_err(|error| SQLError::Internal(format!("resolve table `{table}`: {error}")))?
115 .ok_or_else(|| SQLError::UnknownTable(table.to_string()))
116}
117
118#[cfg(test)]
119mod tests;