1use crate::types::ObjectKind;
19use mz_sql_parser::ast::*;
20
21#[derive(Debug)]
31pub struct DatabaseIdent {
32 pub database: Option<Ident>,
33 pub schema: Option<Ident>,
34 pub object: Ident,
35}
36
37impl From<UnresolvedItemName> for DatabaseIdent {
38 fn from(value: UnresolvedItemName) -> Self {
39 match value.0.as_slice() {
40 [object] => Self {
41 database: None,
42 schema: None,
43 object: object.clone(),
44 },
45 [schema, object] => Self {
46 database: None,
47 schema: Some(schema.clone()),
48 object: object.clone(),
49 },
50 [database, schema, object] => Self {
51 database: Some(database.clone()),
52 schema: Some(schema.clone()),
53 object: object.clone(),
54 },
55 _ => unreachable!(),
56 }
57 }
58}
59
60impl DatabaseIdent {
61 pub(crate) fn matches(&self, other: &DatabaseIdent) -> bool {
83 if self.object != other.object {
84 return false;
85 }
86
87 if let Some(ref our_schema) = self.schema
89 && let Some(ref their_schema) = other.schema
90 && our_schema != their_schema
91 {
92 return false;
93 }
94
95 if let Some(ref our_db) = self.database
97 && let Some(ref their_db) = other.database
98 && our_db != their_db
99 {
100 return false;
101 }
102
103 true
104 }
105}
106
107#[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
115pub struct Cluster {
116 pub name: String,
117}
118
119impl Cluster {
120 pub fn new(name: String) -> Self {
122 Self { name }
123 }
124}
125
126impl<T: AsRef<str>> From<T> for Cluster {
127 fn from(value: T) -> Self {
128 Self::new(value.as_ref().to_string())
129 }
130}
131
132impl std::fmt::Display for Cluster {
133 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134 write!(f, "{}", self.name)
135 }
136}
137
138#[derive(Debug, Clone, Hash)]
143pub enum Statement {
144 CreateSink(CreateSinkStatement<Raw>),
146 CreateView(CreateViewStatement<Raw>),
148 CreateMaterializedView(CreateMaterializedViewStatement<Raw>),
150 CreateTable(CreateTableStatement<Raw>),
152 CreateTableFromSource(CreateTableFromSourceStatement<Raw>),
154 CreateSource(CreateSourceStatement<Raw>),
156 CreateSecret(CreateSecretStatement<Raw>),
158 CreateConnection(CreateConnectionStatement<Raw>),
160}
161
162impl Statement {
163 pub fn kind(&self) -> ObjectKind {
165 match self {
166 Statement::CreateTable(_) | Statement::CreateTableFromSource(_) => ObjectKind::Table,
167 Statement::CreateView(_) => ObjectKind::View,
168 Statement::CreateMaterializedView(_) => ObjectKind::MaterializedView,
169 Statement::CreateSource(_) => ObjectKind::Source,
170 Statement::CreateSink(_) => ObjectKind::Sink,
171 Statement::CreateSecret(_) => ObjectKind::Secret,
172 Statement::CreateConnection(_) => ObjectKind::Connection,
173 }
174 }
175
176 pub fn into_parser_statement(self) -> mz_sql_parser::ast::Statement<Raw> {
178 use mz_sql_parser::ast::Statement as Parser;
179 match self {
180 Statement::CreateSink(s) => Parser::CreateSink(s),
181 Statement::CreateView(s) => Parser::CreateView(s),
182 Statement::CreateMaterializedView(s) => Parser::CreateMaterializedView(s),
183 Statement::CreateTable(s) => Parser::CreateTable(s),
184 Statement::CreateTableFromSource(s) => Parser::CreateTableFromSource(s),
185 Statement::CreateSource(s) => Parser::CreateSource(s),
186 Statement::CreateSecret(s) => Parser::CreateSecret(s),
187 Statement::CreateConnection(s) => Parser::CreateConnection(s),
188 }
189 }
190
191 pub fn ident(&self) -> DatabaseIdent {
196 match self {
197 Statement::CreateSink(s) => s
198 .name
199 .clone()
200 .expect("CREATE SINK statement should have a name")
201 .into(),
202 Statement::CreateView(v) => v.definition.name.clone().into(),
203 Statement::CreateMaterializedView(m) => m.name.clone().into(),
204 Statement::CreateTable(t) => t.name.clone().into(),
205 Statement::CreateTableFromSource(t) => t.name.clone().into(),
206 Statement::CreateSource(s) => s.name.clone().into(),
207 Statement::CreateSecret(s) => s.name.clone().into(),
208 Statement::CreateConnection(c) => c.name.clone().into(),
209 }
210 }
211}
212
213impl std::fmt::Display for Statement {
214 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
215 match self {
216 Statement::CreateSink(s) => write!(f, "{}", s),
217 Statement::CreateView(s) => write!(f, "{}", s),
218 Statement::CreateMaterializedView(s) => write!(f, "{}", s),
219 Statement::CreateTable(s) => write!(f, "{}", s),
220 Statement::CreateTableFromSource(s) => write!(f, "{}", s),
221 Statement::CreateSource(s) => write!(f, "{}", s),
222 Statement::CreateSecret(s) => write!(f, "{}", s),
223 Statement::CreateConnection(c) => write!(f, "{}", c),
224 }
225 }
226}
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231 use std::collections::BTreeSet;
232
233 #[mz_ore::test]
234 fn test_cluster_creation() {
235 let cluster = Cluster::new("quickstart".to_string());
236 assert_eq!(cluster.name, "quickstart");
237 }
238
239 #[mz_ore::test]
240 fn test_cluster_equality() {
241 let c1 = Cluster::new("quickstart".to_string());
242 let c2 = Cluster::new("quickstart".to_string());
243 let c3 = Cluster::new("prod".to_string());
244
245 assert_eq!(c1, c2);
246 assert_ne!(c1, c3);
247 assert_ne!(c2, c3);
248 }
249
250 #[mz_ore::test]
251 fn test_cluster_clone() {
252 let c1 = Cluster::new("quickstart".to_string());
253 let c2 = c1.clone();
254
255 assert_eq!(c1, c2);
256 assert_eq!(c1.name, c2.name);
257 }
258
259 #[mz_ore::test]
260 fn test_cluster_hash_consistency() {
261 use std::collections::hash_map::DefaultHasher;
262 use std::hash::{Hash, Hasher};
263
264 let c1 = Cluster::new("quickstart".to_string());
265 let c2 = Cluster::new("quickstart".to_string());
266
267 let mut hasher1 = DefaultHasher::new();
268 c1.hash(&mut hasher1);
269 let hash1 = hasher1.finish();
270
271 let mut hasher2 = DefaultHasher::new();
272 c2.hash(&mut hasher2);
273 let hash2 = hasher2.finish();
274
275 assert_eq!(hash1, hash2, "Equal clusters should have equal hashes");
276 }
277
278 #[mz_ore::test]
279 fn test_cluster_in_hashset() {
280 let mut clusters = BTreeSet::new();
281
282 assert!(clusters.insert(Cluster::new("quickstart".to_string())));
283 assert!(!clusters.insert(Cluster::new("quickstart".to_string()))); assert!(clusters.insert(Cluster::new("prod".to_string())));
285
286 assert_eq!(clusters.len(), 2);
287 assert!(clusters.contains(&Cluster::new("quickstart".to_string())));
288 assert!(clusters.contains(&Cluster::new("prod".to_string())));
289 assert!(!clusters.contains(&Cluster::new("staging".to_string())));
290 }
291
292 #[mz_ore::test]
293 fn test_database_ident_matches() {
294 let ident1 = DatabaseIdent {
296 database: None,
297 schema: None,
298 object: Ident::new_unchecked("table"),
299 };
300
301 let ident2 = DatabaseIdent {
302 database: Some(Ident::new_unchecked("db")),
303 schema: Some(Ident::new_unchecked("public")),
304 object: Ident::new_unchecked("table"),
305 };
306
307 assert!(ident1.matches(&ident2));
308
309 let ident3 = DatabaseIdent {
311 database: None,
312 schema: Some(Ident::new_unchecked("public")),
313 object: Ident::new_unchecked("table"),
314 };
315
316 assert!(ident3.matches(&ident2));
317
318 let ident4 = DatabaseIdent {
320 database: None,
321 schema: Some(Ident::new_unchecked("private")),
322 object: Ident::new_unchecked("table"),
323 };
324
325 assert!(!ident4.matches(&ident2));
326 }
327}