1use snafu::{ResultExt, ensure};
16use sqlparser::dialect::keywords::Keyword;
17use sqlparser::tokenizer::Token;
18
19use crate::error::{self, InvalidFlowNameSnafu, InvalidTableNameSnafu, Result};
20use crate::parser::{FLOW, ParserContext};
21#[cfg(feature = "enterprise")]
22use crate::statements::drop::UndropTable;
23#[cfg(feature = "enterprise")]
24use crate::statements::drop::trigger::DropTrigger;
25use crate::statements::drop::{DropDatabase, DropFlow, DropTable, DropView};
26use crate::statements::statement::Statement;
27
28impl ParserContext<'_> {
30 #[cfg(feature = "enterprise")]
31 pub(crate) fn parse_undrop_table(&mut self) -> Result<Statement> {
32 let _ = self.parser.next_token();
33 if !self.parser.parse_keyword(Keyword::TABLE) {
34 return self.expected("TABLE", self.parser.peek_token());
35 }
36
37 let raw_table_name = self
38 .parse_object_name()
39 .with_context(|_| error::UnexpectedSnafu {
40 expected: "a table name",
41 actual: self.peek_token_as_string(),
42 })?;
43 let table_name = Self::canonicalize_object_name(raw_table_name)?;
44 ensure!(
45 !table_name.0.is_empty(),
46 InvalidTableNameSnafu {
47 name: table_name.to_string()
48 }
49 );
50
51 Ok(Statement::UndropTable(UndropTable::new(table_name)))
52 }
53
54 pub(crate) fn parse_drop(&mut self) -> Result<Statement> {
55 let _ = self.parser.next_token();
56 match self.parser.peek_token().token {
57 Token::Word(w) => match w.keyword {
58 Keyword::TABLE => self.parse_drop_table(),
59 Keyword::VIEW => self.parse_drop_view(),
60 #[cfg(feature = "enterprise")]
61 Keyword::TRIGGER => self.parse_drop_trigger(),
62 Keyword::SCHEMA | Keyword::DATABASE => self.parse_drop_database(),
63 Keyword::NoKeyword => {
64 let uppercase = w.value.to_uppercase();
65 match uppercase.as_str() {
66 FLOW => self.parse_drop_flow(),
67 _ => self.unsupported(w.to_string()),
68 }
69 }
70 _ => self.unsupported(w.to_string()),
71 },
72 unexpected => self.unsupported(unexpected.to_string()),
73 }
74 }
75
76 #[cfg(feature = "enterprise")]
77 fn parse_drop_trigger(&mut self) -> Result<Statement> {
78 let _ = self.parser.next_token();
79
80 let if_exists = self.parser.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
81 let raw_trigger_ident =
82 self.parse_object_name()
83 .with_context(|_| error::UnexpectedSnafu {
84 expected: "a trigger name",
85 actual: self.peek_token_as_string(),
86 })?;
87 let trigger_ident = Self::canonicalize_object_name(raw_trigger_ident)?;
88 ensure!(
89 !trigger_ident.0.is_empty(),
90 error::InvalidTriggerNameSnafu {
91 name: trigger_ident.to_string()
92 }
93 );
94
95 Ok(Statement::DropTrigger(DropTrigger::new(
96 trigger_ident,
97 if_exists,
98 )))
99 }
100
101 fn parse_drop_view(&mut self) -> Result<Statement> {
102 let _ = self.parser.next_token();
103
104 let if_exists = self.parser.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
105 let raw_view_ident = self
106 .parse_object_name()
107 .with_context(|_| error::UnexpectedSnafu {
108 expected: "a view name",
109 actual: self.peek_token_as_string(),
110 })?;
111 let view_ident = Self::canonicalize_object_name(raw_view_ident)?;
112 ensure!(
113 !view_ident.0.is_empty(),
114 InvalidTableNameSnafu {
115 name: view_ident.to_string()
116 }
117 );
118
119 Ok(Statement::DropView(DropView {
120 view_name: view_ident,
121 drop_if_exists: if_exists,
122 }))
123 }
124
125 fn parse_drop_flow(&mut self) -> Result<Statement> {
126 let _ = self.parser.next_token();
127
128 let if_exists = self.parser.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
129 let raw_flow_ident = self
130 .parse_object_name()
131 .with_context(|_| error::UnexpectedSnafu {
132 expected: "a flow name",
133 actual: self.peek_token_as_string(),
134 })?;
135 let flow_ident = Self::canonicalize_object_name(raw_flow_ident)?;
136 ensure!(
137 !flow_ident.0.is_empty(),
138 InvalidFlowNameSnafu {
139 name: flow_ident.to_string()
140 }
141 );
142
143 Ok(Statement::DropFlow(DropFlow::new(flow_ident, if_exists)))
144 }
145
146 fn parse_drop_table(&mut self) -> Result<Statement> {
147 let _ = self.parser.next_token();
148
149 let if_exists = self.parser.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
150 let mut table_names = Vec::with_capacity(1);
151 loop {
152 let raw_table_ident =
153 self.parse_object_name()
154 .with_context(|_| error::UnexpectedSnafu {
155 expected: "a table name",
156 actual: self.peek_token_as_string(),
157 })?;
158 let table_ident = Self::canonicalize_object_name(raw_table_ident)?;
159 ensure!(
160 !table_ident.0.is_empty(),
161 InvalidTableNameSnafu {
162 name: table_ident.to_string()
163 }
164 );
165 table_names.push(table_ident);
166 if !self.parser.consume_token(&Token::Comma) {
167 break;
168 }
169 }
170
171 Ok(Statement::DropTable(DropTable::new(table_names, if_exists)))
172 }
173
174 fn parse_drop_database(&mut self) -> Result<Statement> {
175 let _ = self.parser.next_token();
176
177 let if_exists = self.parser.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
178 let database_name = self
179 .parse_object_name()
180 .with_context(|_| error::UnexpectedSnafu {
181 expected: "a database name",
182 actual: self.peek_token_as_string(),
183 })?;
184 let database_name = Self::canonicalize_object_name(database_name)?;
185
186 Ok(Statement::DropDatabase(DropDatabase::new(
187 database_name,
188 if_exists,
189 )))
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use sqlparser::ast::{Ident, ObjectName};
196
197 use super::*;
198 use crate::dialect::GreptimeDbDialect;
199 use crate::parser::ParseOptions;
200 #[cfg(feature = "enterprise")]
201 use crate::statements::drop::UndropTable;
202
203 #[cfg(feature = "enterprise")]
204 #[test]
205 fn test_undrop_table() {
206 let cases = [
207 ("UNDROP TABLE foo", "foo"),
208 ("undrop table my_schema.FOO", "my_schema.foo"),
209 (
210 "UnDrOp TaBlE my_catalog.my_schema.foo",
211 "my_catalog.my_schema.foo",
212 ),
213 (
214 "UNDROP TABLE `My Catalog`.`My Schema`.`My Table`",
215 "`My Catalog`.`My Schema`.`My Table`",
216 ),
217 ];
218
219 for (sql, expected_name) in cases {
220 let mut stmts = ParserContext::create_with_dialect(
221 sql,
222 &GreptimeDbDialect {},
223 ParseOptions::default(),
224 )
225 .unwrap();
226 let stmt = stmts.pop().unwrap();
227 assert_eq!(
228 stmt,
229 Statement::UndropTable(UndropTable::new(
230 ParserContext::parse_table_name(expected_name, &GreptimeDbDialect {}).unwrap()
231 ))
232 );
233 assert_eq!(format!("UNDROP TABLE {expected_name}"), stmt.to_string());
234 assert!(!stmt.is_readonly());
235 }
236 }
237
238 #[test]
239 fn test_undrop_table_rejects_unsupported_syntax() {
240 for sql in [
241 "UNDROP",
242 "UNDROP foo",
243 "UNDROP TABLE",
244 "UNDROP TABLE IF EXISTS foo",
245 "UNDROP TABLE foo, bar",
246 "UNDROP TABLE foo bar",
247 "UNDROP TABLE foo RENAME TO bar",
248 "UNDROP TABLE foo AT '2026-01-01'",
249 ] {
250 assert!(
251 ParserContext::create_with_dialect(
252 sql,
253 &GreptimeDbDialect {},
254 ParseOptions::default(),
255 )
256 .is_err(),
257 "unexpectedly parsed: {sql}"
258 );
259 }
260 }
261
262 #[test]
263 pub fn test_drop_table() {
264 let sql = "DROP TABLE foo";
265 let result =
266 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
267 let mut stmts = result.unwrap();
268 assert_eq!(
269 stmts.pop().unwrap(),
270 Statement::DropTable(DropTable::new(
271 vec![ObjectName::from(vec![Ident::new("foo")])],
272 false
273 ))
274 );
275
276 let sql = "DROP TABLE IF EXISTS foo";
277 let result =
278 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
279 let mut stmts = result.unwrap();
280 assert_eq!(
281 stmts.pop().unwrap(),
282 Statement::DropTable(DropTable::new(
283 vec![ObjectName::from(vec![Ident::new("foo")])],
284 true
285 ))
286 );
287
288 let sql = "DROP TABLE my_schema.foo";
289 let result =
290 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
291 let mut stmts = result.unwrap();
292 assert_eq!(
293 stmts.pop().unwrap(),
294 Statement::DropTable(DropTable::new(
295 vec![ObjectName::from(vec![
296 Ident::new("my_schema"),
297 Ident::new("foo")
298 ])],
299 false
300 ))
301 );
302
303 let sql = "DROP TABLE my_catalog.my_schema.foo";
304 let result =
305 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
306 let mut stmts = result.unwrap();
307 assert_eq!(
308 stmts.pop().unwrap(),
309 Statement::DropTable(DropTable::new(
310 vec![ObjectName::from(vec![
311 Ident::new("my_catalog"),
312 Ident::new("my_schema"),
313 Ident::new("foo")
314 ])],
315 false
316 ))
317 )
318 }
319
320 #[test]
321 pub fn test_drop_database() {
322 let sql = "DROP DATABASE public";
323 let result =
324 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
325 let mut stmts = result.unwrap();
326 assert_eq!(
327 stmts.pop().unwrap(),
328 Statement::DropDatabase(DropDatabase::new(
329 ObjectName::from(vec![Ident::new("public")]),
330 false
331 ))
332 );
333
334 let sql = "DROP DATABASE IF EXISTS public";
335 let result =
336 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
337 let mut stmts = result.unwrap();
338 assert_eq!(
339 stmts.pop().unwrap(),
340 Statement::DropDatabase(DropDatabase::new(
341 ObjectName::from(vec![Ident::new("public")]),
342 true
343 ))
344 );
345
346 let sql = "DROP DATABASE `fOo`";
347 let result =
348 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
349 let mut stmts = result.unwrap();
350 assert_eq!(
351 stmts.pop().unwrap(),
352 Statement::DropDatabase(DropDatabase::new(
353 ObjectName::from(vec![Ident::with_quote('`', "fOo"),]),
354 false
355 ))
356 );
357 }
358
359 #[test]
360 pub fn test_drop_flow() {
361 let sql = "DROP FLOW foo";
362 let result =
363 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
364 let mut stmts: Vec<Statement> = result.unwrap();
365 assert_eq!(
366 stmts.pop().unwrap(),
367 Statement::DropFlow(DropFlow::new(
368 ObjectName::from(vec![Ident::new("foo")]),
369 false
370 ))
371 );
372
373 let sql = "DROP FLOW IF EXISTS foo";
374 let result =
375 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
376 let mut stmts = result.unwrap();
377 assert_eq!(
378 stmts.pop().unwrap(),
379 Statement::DropFlow(DropFlow::new(
380 ObjectName::from(vec![Ident::new("foo")]),
381 true
382 ))
383 );
384
385 let sql = "DROP FLOW my_schema.foo";
386 let result =
387 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
388 let mut stmts = result.unwrap();
389 assert_eq!(
390 stmts.pop().unwrap(),
391 Statement::DropFlow(DropFlow::new(
392 ObjectName::from(vec![Ident::new("my_schema"), Ident::new("foo")]),
393 false
394 ))
395 );
396
397 let sql = "DROP FLOW my_catalog.my_schema.foo";
398 let result =
399 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
400 let mut stmts = result.unwrap();
401 assert_eq!(
402 stmts.pop().unwrap(),
403 Statement::DropFlow(DropFlow::new(
404 ObjectName::from(vec![
405 Ident::new("my_catalog"),
406 Ident::new("my_schema"),
407 Ident::new("foo")
408 ]),
409 false
410 ))
411 )
412 }
413
414 #[test]
415 pub fn test_drop_view() {
416 let sql = "DROP VIEW foo";
417 let result =
418 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
419 let mut stmts: Vec<Statement> = result.unwrap();
420 let stmt = stmts.pop().unwrap();
421 assert_eq!(
422 stmt,
423 Statement::DropView(DropView {
424 view_name: ObjectName::from(vec![Ident::new("foo")]),
425 drop_if_exists: false,
426 })
427 );
428 assert_eq!(sql, stmt.to_string());
429
430 let sql = "DROP VIEW greptime.public.foo";
431 let result =
432 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
433 let mut stmts: Vec<Statement> = result.unwrap();
434 let stmt = stmts.pop().unwrap();
435 assert_eq!(
436 stmt,
437 Statement::DropView(DropView {
438 view_name: ObjectName::from(vec![
439 Ident::new("greptime"),
440 Ident::new("public"),
441 Ident::new("foo")
442 ]),
443 drop_if_exists: false,
444 })
445 );
446 assert_eq!(sql, stmt.to_string());
447
448 let sql = "DROP VIEW IF EXISTS foo";
449 let result =
450 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
451 let mut stmts: Vec<Statement> = result.unwrap();
452 let stmt = stmts.pop().unwrap();
453 assert_eq!(
454 stmt,
455 Statement::DropView(DropView {
456 view_name: ObjectName::from(vec![Ident::new("foo")]),
457 drop_if_exists: true,
458 })
459 );
460 assert_eq!(sql, stmt.to_string());
461 }
462}