Skip to main content

sql/parsers/
drop_parser.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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
28/// DROP statement parser implementation
29impl 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}