uqa_sql/semantics/rules/action_binding/
context.rs1use super::{apply_positional_aliases, cte_output_columns, BTreeMap, SQLError, CTE};
9use crate::ast::{ColumnType, Statement};
10
11pub trait RuleSourceCatalog {
12 fn query_source_columns(
13 &self,
14 name: &str,
15 relations_bound: bool,
16 ) -> Result<Option<Vec<String>>, SQLError>;
17 fn rule_relation_columns(&self, name: &str) -> Result<Vec<(String, ColumnType)>, SQLError>;
18}
19
20#[derive(Clone, Default)]
21pub(super) struct RuleBindingContext<'a> {
22 pub(super) catalog: Option<&'a dyn RuleSourceCatalog>,
23 pub(super) relations_bound: bool,
24 pub(super) ctes: BTreeMap<String, Vec<String>>,
25}
26
27impl<'a> RuleBindingContext<'a> {
28 pub(super) fn with_catalog(catalog: &'a dyn RuleSourceCatalog, relations_bound: bool) -> Self {
29 Self {
30 catalog: Some(catalog),
31 relations_bound,
32 ctes: BTreeMap::new(),
33 }
34 }
35
36 pub(super) fn relation_columns(&self, name: &str) -> Result<Vec<String>, SQLError> {
37 if let Some(columns) = self.ctes.get(&name.to_ascii_lowercase()) {
38 return Ok(columns.clone());
39 }
40 self.catalog.map_or_else(
41 || Ok(Vec::new()),
42 |catalog| {
43 catalog
44 .query_source_columns(name, self.relations_bound)?
45 .ok_or_else(|| SQLError::UnknownTable(name.to_string()))
46 },
47 )
48 }
49
50 pub(super) fn with_ctes(&self, ctes: &[CTE]) -> Result<Self, SQLError> {
51 let mut context = self.clone();
52 for cte in ctes {
53 let key = cte.name.to_ascii_lowercase();
54 if cte.recursive {
55 context.ctes.entry(key.clone()).or_default();
56 }
57 let mut columns = cte_output_columns(&cte.body, &context)?;
58 apply_positional_aliases(&mut columns, &cte.columns);
59 if let Some(search) = &cte.search {
60 columns.push(search.sequence_column.clone());
61 }
62 if let Some(cycle) = &cte.cycle {
63 columns.push(cycle.mark_column.clone());
64 columns.push(cycle.path_column.clone());
65 }
66 context.ctes.insert(key, columns);
67 }
68 Ok(context)
69 }
70}
71
72pub fn rule_action_target_row_type(
73 catalog: &dyn RuleSourceCatalog,
74 action: &Statement,
75) -> Result<Vec<(String, ColumnType)>, SQLError> {
76 let table = match action {
77 Statement::Insert(statement) => &statement.table,
78 Statement::Update(statement) => &statement.table,
79 Statement::Delete(statement) => &statement.table,
80 _ => return Ok(Vec::new()),
81 };
82 catalog.rule_relation_columns(table)
83}
84pub fn rule_action_target_columns(
85 catalog: &dyn RuleSourceCatalog,
86 action: &Statement,
87) -> Result<std::collections::BTreeSet<String>, SQLError> {
88 Ok(rule_action_target_row_type(catalog, action)?
89 .into_iter()
90 .map(|(column, _)| column)
91 .collect())
92}