vibesql_catalog/store/advanced/
views.rs1use crate::{errors::CatalogError, view::ViewDefinition};
4
5#[derive(Debug, Clone, Copy, PartialEq)]
7pub enum ViewDropBehavior {
8 Cascade,
10 Restrict,
12 Silent,
14}
15
16impl super::super::Catalog {
17 pub fn create_view(&mut self, view: ViewDefinition) -> Result<(), CatalogError> {
23 let name = view.name.clone();
24
25 let key = if self.case_sensitive_identifiers { name.clone() } else { name.to_uppercase() };
27
28 if self.views.contains_key(&key) {
30 return Err(CatalogError::ViewAlreadyExists(name));
31 }
32
33 self.views.insert(key, view);
34 Ok(())
35 }
36
37 pub fn get_view(&self, name: &str) -> Option<&ViewDefinition> {
39 let key =
41 if self.case_sensitive_identifiers { name.to_string() } else { name.to_uppercase() };
42
43 self.views.get(&key)
44 }
45
46 pub fn list_views(&self) -> Vec<String> {
48 self.views.values().map(|v| v.name.clone()).collect()
49 }
50
51 pub fn drop_view_with_behavior(
57 &mut self,
58 name: &str,
59 behavior: ViewDropBehavior,
60 ) -> Result<(), CatalogError> {
61 let key =
63 if self.case_sensitive_identifiers { name.to_string() } else { name.to_uppercase() };
64
65 if !self.views.contains_key(&key) {
67 return Err(CatalogError::ViewNotFound(name.to_string()));
68 }
69
70 match behavior {
71 ViewDropBehavior::Cascade => {
72 let dependent_views = self.find_dependent_views(name);
75 let views_to_drop = dependent_views.clone();
76 for dependent_view in views_to_drop {
77 self.drop_view_with_behavior(&dependent_view, ViewDropBehavior::Cascade)?;
79 }
80 }
81 ViewDropBehavior::Restrict => {
82 let dependent_views = self.find_dependent_views(name);
84 if !dependent_views.is_empty() {
85 return Err(CatalogError::ViewInUse {
86 view_name: name.to_string(),
87 dependent_views,
88 });
89 }
90 }
91 ViewDropBehavior::Silent => {
92 }
95 }
96
97 self.views.remove(&key);
99 Ok(())
100 }
101
102 pub fn drop_view(&mut self, name: &str, cascade: bool) -> Result<(), CatalogError> {
107 let behavior = if cascade { ViewDropBehavior::Cascade } else { ViewDropBehavior::Restrict };
108 self.drop_view_with_behavior(name, behavior)
109 }
110
111 fn find_dependent_views(&self, target_name: &str) -> Vec<String> {
113 let mut dependent_views = Vec::new();
114
115 let target_key = if self.case_sensitive_identifiers {
117 target_name.to_string()
118 } else {
119 target_name.to_uppercase()
120 };
121
122 for (view_name, view_def) in &self.views {
123 if view_name == &target_key {
124 continue;
126 }
127
128 if self.select_references_table(&view_def.query, target_name) {
130 dependent_views.push(view_name.clone());
131 }
132 }
133
134 dependent_views
135 }
136
137 fn select_references_table(&self, select: &vibesql_ast::SelectStmt, table_name: &str) -> bool {
139 if let Some(ref from) = select.from {
141 if self.does_from_clause_reference_table(from, table_name) {
142 return true;
143 }
144 }
145
146 if let Some(ref ctes) = select.with_clause {
148 for cte in ctes {
149 if self.select_references_table(&cte.query, table_name) {
150 return true;
151 }
152 }
153 }
154
155 if let Some(ref set_op) = select.set_operation {
157 if self.select_references_table(&set_op.right, table_name) {
158 return true;
159 }
160 }
161
162 false
163 }
164
165 fn does_from_clause_reference_table(
167 &self,
168 from: &vibesql_ast::FromClause,
169 table_name: &str,
170 ) -> bool {
171 use vibesql_ast::FromClause;
172 match from {
173 FromClause::Table { name, .. } => {
174 if self.case_sensitive_identifiers {
176 name == table_name
177 } else {
178 name.to_uppercase() == table_name.to_uppercase()
179 }
180 }
181 FromClause::Join { left, right, .. } => {
182 self.does_from_clause_reference_table(left, table_name)
183 || self.does_from_clause_reference_table(right, table_name)
184 }
185 FromClause::Subquery { query, .. } => self.select_references_table(query, table_name),
186 FromClause::Values { .. } => false,
188 }
189 }
190}