scylladb_parse/statements/
views.rs1use super::*;
2use crate::PrimaryKey;
3
4#[derive(ParseFromStr, Clone, Debug, TryInto, From, ToTokens, PartialEq)]
5#[parse_via(TaggedMaterializedViewStatement)]
6pub enum MaterializedViewStatement {
7 Create(CreateMaterializedViewStatement),
8 Alter(AlterMaterializedViewStatement),
9 Drop(DropMaterializedViewStatement),
10}
11
12impl TryFrom<TaggedMaterializedViewStatement> for MaterializedViewStatement {
13 type Error = anyhow::Error;
14 fn try_from(value: TaggedMaterializedViewStatement) -> Result<Self, Self::Error> {
15 Ok(match value {
16 TaggedMaterializedViewStatement::Create(s) => MaterializedViewStatement::Create(s.try_into()?),
17 TaggedMaterializedViewStatement::Alter(s) => MaterializedViewStatement::Alter(s.try_into()?),
18 TaggedMaterializedViewStatement::Drop(s) => MaterializedViewStatement::Drop(s.try_into()?),
19 })
20 }
21}
22
23#[derive(ParseFromStr, Clone, Debug, TryInto, From, ToTokens, PartialEq)]
24#[tokenize_as(MaterializedViewStatement)]
25pub enum TaggedMaterializedViewStatement {
26 Create(TaggedCreateMaterializedViewStatement),
27 Alter(TaggedAlterMaterializedViewStatement),
28 Drop(TaggedDropMaterializedViewStatement),
29}
30
31impl Parse for TaggedMaterializedViewStatement {
32 type Output = Self;
33 fn parse(s: &mut StatementStream<'_>) -> anyhow::Result<Self::Output> {
34 Ok(if s.check::<CREATE>() {
35 Self::Create(s.parse()?)
36 } else if s.check::<ALTER>() {
37 Self::Alter(s.parse()?)
38 } else if s.check::<DROP>() {
39 Self::Drop(s.parse()?)
40 } else {
41 anyhow::bail!("Expected a materialized view statement, found {}", s.info())
42 })
43 }
44}
45
46impl Display for MaterializedViewStatement {
47 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
48 match self {
49 Self::Create(stmt) => stmt.fmt(f),
50 Self::Alter(stmt) => stmt.fmt(f),
51 Self::Drop(stmt) => stmt.fmt(f),
52 }
53 }
54}
55
56#[derive(ParseFromStr, Builder, Clone, Debug, ToTokens, PartialEq)]
57#[parse_via(TaggedCreateMaterializedViewStatement)]
58#[builder(setter(strip_option))]
59pub struct CreateMaterializedViewStatement {
60 #[builder(setter(name = "set_if_not_exists"), default)]
61 pub if_not_exists: bool,
62 #[builder(setter(into))]
63 pub name: KeyspaceQualifiedName,
64 pub select_statement: SelectStatement,
65 #[builder(setter(into))]
66 pub primary_key: PrimaryKey,
67 #[builder(default)]
68 pub table_opts: Option<TableOpts>,
69}
70
71impl TryFrom<TaggedCreateMaterializedViewStatement> for CreateMaterializedViewStatement {
72 type Error = anyhow::Error;
73 fn try_from(value: TaggedCreateMaterializedViewStatement) -> Result<Self, Self::Error> {
74 Ok(Self {
75 if_not_exists: value.if_not_exists,
76 name: value.name.try_into()?,
77 select_statement: value.select_statement.into_value()?.try_into()?,
78 primary_key: value.primary_key.into_value()?,
79 table_opts: value.table_opts.map(|v| v.into_value()).transpose()?,
80 })
81 }
82}
83
84#[derive(ParseFromStr, Builder, Clone, Debug, ToTokens, PartialEq)]
85#[tokenize_as(CreateMaterializedViewStatement)]
86#[builder(setter(strip_option))]
87pub struct TaggedCreateMaterializedViewStatement {
88 #[builder(setter(name = "set_if_not_exists"), default)]
89 pub if_not_exists: bool,
90 pub name: TaggedKeyspaceQualifiedName,
91 pub select_statement: Tag<TaggedSelectStatement>,
92 pub primary_key: Tag<PrimaryKey>,
93 #[builder(default)]
94 pub table_opts: Option<Tag<TableOpts>>,
95}
96
97impl CreateMaterializedViewStatementBuilder {
98 pub fn if_not_exists(&mut self) -> &mut Self {
101 self.if_not_exists.replace(true);
102 self
103 }
104}
105
106impl Parse for TaggedCreateMaterializedViewStatement {
107 type Output = Self;
108 fn parse(s: &mut StatementStream<'_>) -> anyhow::Result<Self::Output> {
109 s.parse::<(CREATE, MATERIALIZED, VIEW)>()?;
110 let mut res = TaggedCreateMaterializedViewStatementBuilder::default();
111 res.set_if_not_exists(s.parse::<Option<(IF, NOT, EXISTS)>>()?.is_some())
112 .name(s.parse()?)
113 .select_statement(s.parse::<(AS, _)>()?.1)
114 .primary_key(s.parse_from::<((PRIMARY, KEY), Parens<Tag<PrimaryKey>>)>()?.1);
115 if let Some(p) = s.parse_from::<If<WITH, Tag<TableOpts>>>()? {
116 res.table_opts(p);
117 }
118 s.parse::<Option<Semicolon>>()?;
119 Ok(res
120 .build()
121 .map_err(|e| anyhow::anyhow!("Invalid CREATE MATERIALIZED VIEW statement: {}", e))?)
122 }
123}
124
125impl Display for CreateMaterializedViewStatement {
126 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
127 write!(
128 f,
129 "CREATE MATERIALIZED VIEW{} {} AS {} PRIMARY KEY ({}){}",
130 if self.if_not_exists { " IF NOT EXISTS" } else { "" },
131 self.name,
132 self.select_statement,
133 self.primary_key,
134 match self.table_opts.as_ref() {
135 Some(opts) => format!(" WITH {}", opts),
136 None => "".to_string(),
137 }
138 )
139 }
140}
141
142#[derive(ParseFromStr, Builder, Clone, Debug, ToTokens, PartialEq)]
143#[parse_via(TaggedAlterMaterializedViewStatement)]
144pub struct AlterMaterializedViewStatement {
145 #[builder(setter(into))]
146 pub name: KeyspaceQualifiedName,
147 pub table_opts: TableOpts,
148}
149
150impl TryFrom<TaggedAlterMaterializedViewStatement> for AlterMaterializedViewStatement {
151 type Error = anyhow::Error;
152 fn try_from(value: TaggedAlterMaterializedViewStatement) -> Result<Self, Self::Error> {
153 Ok(Self {
154 name: value.name.try_into()?,
155 table_opts: value.table_opts.into_value()?,
156 })
157 }
158}
159
160#[derive(ParseFromStr, Builder, Clone, Debug, ToTokens, PartialEq)]
161#[tokenize_as(AlterMaterializedViewStatement)]
162pub struct TaggedAlterMaterializedViewStatement {
163 pub name: TaggedKeyspaceQualifiedName,
164 pub table_opts: Tag<TableOpts>,
165}
166
167impl Parse for TaggedAlterMaterializedViewStatement {
168 type Output = Self;
169 fn parse(s: &mut StatementStream<'_>) -> anyhow::Result<Self::Output> {
170 s.parse::<(ALTER, MATERIALIZED, VIEW)>()?;
171 let mut res = TaggedAlterMaterializedViewStatementBuilder::default();
172 res.name(s.parse()?).table_opts(s.parse::<(WITH, _)>()?.1);
173 s.parse::<Option<Semicolon>>()?;
174 Ok(res
175 .build()
176 .map_err(|e| anyhow::anyhow!("Invalid ALTER MATERIALIZED VIEW statement: {}", e))?)
177 }
178}
179
180impl Display for AlterMaterializedViewStatement {
181 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
182 write!(f, "ALTER MATERIALIZED VIEW {} WITH {}", self.name, self.table_opts)
183 }
184}
185
186#[derive(ParseFromStr, Builder, Clone, Debug, ToTokens, PartialEq, Eq)]
187#[parse_via(TaggedDropMaterializedViewStatement)]
188pub struct DropMaterializedViewStatement {
189 #[builder(setter(name = "set_if_exists"), default)]
190 pub if_exists: bool,
191 #[builder(setter(into))]
192 pub name: KeyspaceQualifiedName,
193}
194
195impl TryFrom<TaggedDropMaterializedViewStatement> for DropMaterializedViewStatement {
196 type Error = anyhow::Error;
197 fn try_from(value: TaggedDropMaterializedViewStatement) -> Result<Self, Self::Error> {
198 Ok(Self {
199 if_exists: value.if_exists,
200 name: value.name.try_into()?,
201 })
202 }
203}
204
205#[derive(ParseFromStr, Builder, Clone, Debug, ToTokens, PartialEq, Eq)]
206#[tokenize_as(DropMaterializedViewStatement)]
207pub struct TaggedDropMaterializedViewStatement {
208 #[builder(setter(name = "set_if_exists"), default)]
209 pub if_exists: bool,
210 pub name: TaggedKeyspaceQualifiedName,
211}
212
213impl DropMaterializedViewStatementBuilder {
214 pub fn if_exists(&mut self) -> &mut Self {
217 self.if_exists.replace(true);
218 self
219 }
220}
221
222impl Parse for TaggedDropMaterializedViewStatement {
223 type Output = Self;
224 fn parse(s: &mut StatementStream<'_>) -> anyhow::Result<Self::Output> {
225 s.parse::<(DROP, MATERIALIZED, VIEW)>()?;
226 let mut res = TaggedDropMaterializedViewStatementBuilder::default();
227 res.set_if_exists(s.parse::<Option<(IF, EXISTS)>>()?.is_some())
228 .name(s.parse()?);
229 s.parse::<Option<Semicolon>>()?;
230 Ok(res
231 .build()
232 .map_err(|e| anyhow::anyhow!("Invalid DROP MATERIALIZED VIEW statement: {}", e))?)
233 }
234}
235
236impl Display for DropMaterializedViewStatement {
237 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
238 write!(
239 f,
240 "DROP MATERIALIZED VIEW{} {}",
241 if self.if_exists { " IF EXISTS" } else { "" },
242 self.name
243 )
244 }
245}
246
247#[cfg(test)]
248mod test {
249 use super::*;
250 use crate::KeyspaceQualifyExt;
251
252 #[test]
253 fn test_parse_create_mv() {
254 let mut builder = CreateMaterializedViewStatementBuilder::default();
255 builder.name("my_keyspace".dot("test_mv"));
256 assert!(builder.build().is_err());
257 builder.select_statement(
258 SelectStatementBuilder::default()
259 .select_clause(SelectClause::All)
260 .from("test_table")
261 .where_clause(vec![
262 Relation::is_not_null("column_1"),
263 Relation::is_not_null("column 2"),
264 ])
265 .build()
266 .unwrap(),
267 );
268 assert!(builder.build().is_err());
269 builder.primary_key(vec!["column_1", "column 2"]);
270 let statement = builder.build().unwrap().to_string();
271 assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
272 builder.table_opts(
273 crate::TableOptsBuilder::default()
274 .comment(r#"test comment " "#)
275 .build()
276 .unwrap(),
277 );
278 let statement = builder.build().unwrap().to_string();
279 assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
280 builder.primary_key(PrimaryKey::partition_key("column_1").clustering_columns(vec!["column 2", "column_3"]));
281 let statement = builder.build().unwrap().to_string();
282 assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
283 }
284
285 #[test]
286 fn test_parse_alter_mv() {
287 let mut builder = AlterMaterializedViewStatementBuilder::default();
288 builder.name("test mv");
289 assert!(builder.build().is_err());
290 builder.table_opts(
291 crate::TableOptsBuilder::default()
292 .default_time_to_live(100)
293 .build()
294 .unwrap(),
295 );
296 let statement = builder.build().unwrap().to_string();
297 assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
298 }
299
300 #[test]
301 fn test_parse_drop_mv() {
302 let mut builder = DropMaterializedViewStatementBuilder::default();
303 builder.name("test_mv");
304 let statement = builder.build().unwrap().to_string();
305 assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
306 builder.if_exists();
307 let statement = builder.build().unwrap().to_string();
308 assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
309 }
310}