sql/parsers/
show_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
15#[cfg(feature = "enterprise")]
16pub mod trigger;
17
18use snafu::{ensure, ResultExt};
19use sqlparser::keywords::Keyword;
20use sqlparser::tokenizer::Token;
21
22use crate::error::{
23    self, InvalidDatabaseNameSnafu, InvalidFlowNameSnafu, InvalidTableNameSnafu, Result,
24};
25use crate::parser::ParserContext;
26use crate::statements::show::{
27    ShowColumns, ShowCreateDatabase, ShowCreateFlow, ShowCreateTable, ShowCreateTableVariant,
28    ShowCreateView, ShowDatabases, ShowFlows, ShowIndex, ShowKind, ShowProcessList, ShowRegion,
29    ShowSearchPath, ShowStatus, ShowTableStatus, ShowTables, ShowVariables, ShowViews,
30};
31use crate::statements::statement::Statement;
32
33/// SHOW statement parser implementation
34impl ParserContext<'_> {
35    /// Parses SHOW statements
36    /// todo(hl) support `show settings`/`show create`/`show users` etc.
37    pub(crate) fn parse_show(&mut self) -> Result<Statement> {
38        #[cfg(feature = "enterprise")]
39        if self.consume_token("TRIGGERS") {
40            return self.parse_show_triggers();
41        }
42        if self.consume_token("DATABASES") || self.consume_token("SCHEMAS") {
43            self.parse_show_databases(false)
44        } else if self.matches_keyword(Keyword::TABLES) {
45            self.parser.next_token();
46            self.parse_show_tables(false)
47        } else if self.matches_keyword(Keyword::TABLE) {
48            self.parser.next_token();
49            if self.matches_keyword(Keyword::STATUS) {
50                self.parser.next_token();
51                self.parse_show_table_status()
52            } else {
53                self.unsupported(self.peek_token_as_string())
54            }
55        } else if self.consume_token("VIEWS") {
56            self.parse_show_views()
57        } else if self.consume_token("FLOWS") {
58            self.parse_show_flows()
59        } else if self.matches_keyword(Keyword::CHARSET) {
60            self.parser.next_token();
61            Ok(Statement::ShowCharset(self.parse_show_kind()?))
62        } else if self.matches_keyword(Keyword::CHARACTER) {
63            self.parser.next_token();
64
65            if self.matches_keyword(Keyword::SET) {
66                self.parser.next_token();
67                Ok(Statement::ShowCharset(self.parse_show_kind()?))
68            } else {
69                self.unsupported(self.peek_token_as_string())
70            }
71        } else if self.matches_keyword(Keyword::COLLATION) {
72            self.parser.next_token();
73            Ok(Statement::ShowCollation(self.parse_show_kind()?))
74        } else if self.matches_keyword(Keyword::COLUMNS) || self.matches_keyword(Keyword::FIELDS) {
75            // SHOW {COLUMNS | FIELDS}
76            self.parser.next_token();
77            self.parse_show_columns(false)
78        } else if self.consume_token("INDEX")
79            || self.consume_token("INDEXES")
80            || self.consume_token("KEYS")
81        {
82            // SHOW {INDEX | INDEXES | KEYS}
83            self.parse_show_index()
84        } else if self.consume_token("REGIONS") || self.consume_token("REGION") {
85            // SHOW REGIONS
86            self.parse_show_regions()
87        } else if self.consume_token("CREATE") {
88            if self.consume_token("DATABASE") || self.consume_token("SCHEMA") {
89                self.parse_show_create_database()
90            } else if self.consume_token("TABLE") {
91                self.parse_show_create_table()
92            } else if self.consume_token("FLOW") {
93                self.parse_show_create_flow()
94            } else if self.consume_token("VIEW") {
95                self.parse_show_create_view()
96            } else {
97                self.unsupported(self.peek_token_as_string())
98            }
99        } else if self.consume_token("FULL") {
100            if self.consume_token("TABLES") {
101                self.parse_show_tables(true)
102            } else if self.consume_token("COLUMNS") || self.consume_token("FIELDS") {
103                // SHOW {COLUMNS | FIELDS}
104                self.parse_show_columns(true)
105            } else if self.consume_token("DATABASES") || self.consume_token("SCHEMAS") {
106                self.parse_show_databases(true)
107            } else if self.consume_token("PROCESSLIST") {
108                self.parse_show_processlist(true)
109            } else {
110                self.unsupported(self.peek_token_as_string())
111            }
112        } else if self.consume_token("VARIABLES") {
113            let variable = self
114                .parse_object_name()
115                .with_context(|_| error::UnexpectedSnafu {
116                    expected: "a variable name",
117                    actual: self.peek_token_as_string(),
118                })?;
119            Ok(Statement::ShowVariables(ShowVariables { variable }))
120        } else if self.consume_token("STATUS") {
121            Ok(Statement::ShowStatus(ShowStatus {}))
122        } else if self.consume_token("SEARCH_PATH") {
123            Ok(Statement::ShowSearchPath(ShowSearchPath {}))
124        } else if self.consume_token("PROCESSLIST") {
125            self.parse_show_processlist(false)
126        } else {
127            self.unsupported(self.peek_token_as_string())
128        }
129    }
130
131    fn parse_show_create_database(&mut self) -> Result<Statement> {
132        let raw_database_name =
133            self.parse_object_name()
134                .with_context(|_| error::UnexpectedSnafu {
135                    expected: "a database name",
136                    actual: self.peek_token_as_string(),
137                })?;
138        let database_name = Self::canonicalize_object_name(raw_database_name);
139        ensure!(
140            !database_name.0.is_empty(),
141            InvalidDatabaseNameSnafu {
142                name: database_name.to_string(),
143            }
144        );
145        Ok(Statement::ShowCreateDatabase(ShowCreateDatabase {
146            database_name,
147        }))
148    }
149
150    /// Parse SHOW CREATE TABLE statement
151    fn parse_show_create_table(&mut self) -> Result<Statement> {
152        let raw_table_name = self
153            .parse_object_name()
154            .with_context(|_| error::UnexpectedSnafu {
155                expected: "a table name",
156                actual: self.peek_token_as_string(),
157            })?;
158        let table_name = Self::canonicalize_object_name(raw_table_name);
159        ensure!(
160            !table_name.0.is_empty(),
161            InvalidTableNameSnafu {
162                name: table_name.to_string(),
163            }
164        );
165        let mut variant = ShowCreateTableVariant::Original;
166        if self.consume_token("FOR") {
167            if self.consume_token("POSTGRES_FOREIGN_TABLE") {
168                variant = ShowCreateTableVariant::PostgresForeignTable;
169            } else {
170                self.unsupported(self.peek_token_as_string())?;
171            }
172        }
173
174        Ok(Statement::ShowCreateTable(ShowCreateTable {
175            table_name,
176            variant,
177        }))
178    }
179
180    fn parse_show_create_flow(&mut self) -> Result<Statement> {
181        let raw_flow_name = self
182            .parse_object_name()
183            .with_context(|_| error::UnexpectedSnafu {
184                expected: "a flow name",
185                actual: self.peek_token_as_string(),
186            })?;
187        let flow_name = Self::canonicalize_object_name(raw_flow_name);
188        ensure!(
189            !flow_name.0.is_empty(),
190            InvalidFlowNameSnafu {
191                name: flow_name.to_string(),
192            }
193        );
194        Ok(Statement::ShowCreateFlow(ShowCreateFlow { flow_name }))
195    }
196
197    fn parse_show_create_view(&mut self) -> Result<Statement> {
198        let raw_view_name = self
199            .parse_object_name()
200            .with_context(|_| error::UnexpectedSnafu {
201                expected: "a view name",
202                actual: self.peek_token_as_string(),
203            })?;
204        let view_name = Self::canonicalize_object_name(raw_view_name);
205        ensure!(
206            !view_name.0.is_empty(),
207            InvalidTableNameSnafu {
208                name: view_name.to_string(),
209            }
210        );
211        Ok(Statement::ShowCreateView(ShowCreateView { view_name }))
212    }
213
214    fn parse_show_table_name(&mut self) -> Result<String> {
215        self.parser.next_token();
216        let table_name = self
217            .parse_object_name()
218            .with_context(|_| error::UnexpectedSnafu {
219                expected: "a table name",
220                actual: self.peek_token_as_string(),
221            })?;
222
223        ensure!(
224            table_name.0.len() == 1,
225            InvalidDatabaseNameSnafu {
226                name: table_name.to_string(),
227            }
228        );
229
230        // Safety: already checked above
231        Ok(Self::canonicalize_object_name(table_name).0[0]
232            .value
233            .clone())
234    }
235
236    fn parse_db_name(&mut self) -> Result<Option<String>> {
237        self.parser.next_token();
238        let db_name = self
239            .parse_object_name()
240            .with_context(|_| error::UnexpectedSnafu {
241                expected: "a database name",
242                actual: self.peek_token_as_string(),
243            })?;
244
245        ensure!(
246            db_name.0.len() == 1,
247            InvalidDatabaseNameSnafu {
248                name: db_name.to_string(),
249            }
250        );
251
252        // Safety: already checked above
253        Ok(Some(
254            Self::canonicalize_object_name(db_name).0[0].value.clone(),
255        ))
256    }
257
258    fn parse_show_columns(&mut self, full: bool) -> Result<Statement> {
259        let table = match self.parser.peek_token().token {
260            // SHOW columns {in | FROM} TABLE
261            Token::Word(w) if matches!(w.keyword, Keyword::IN | Keyword::FROM) => {
262                self.parse_show_table_name()?
263            }
264            _ => {
265                return error::UnexpectedTokenSnafu {
266                    expected: "{FROM | IN} table",
267                    actual: self.peek_token_as_string(),
268                }
269                .fail();
270            }
271        };
272
273        let database = match self.parser.peek_token().token {
274            Token::EOF | Token::SemiColon => {
275                return Ok(Statement::ShowColumns(ShowColumns {
276                    kind: ShowKind::All,
277                    table,
278                    database: None,
279                    full,
280                }));
281            }
282
283            // SHOW columns {In | FROM} TABLE {In | FROM} DATABASE
284            Token::Word(w) => match w.keyword {
285                Keyword::IN | Keyword::FROM => self.parse_db_name()?,
286
287                _ => None,
288            },
289            _ => None,
290        };
291
292        let kind = self.parse_show_kind()?;
293
294        Ok(Statement::ShowColumns(ShowColumns {
295            kind,
296            database,
297            table,
298            full,
299        }))
300    }
301
302    fn parse_show_kind(&mut self) -> Result<ShowKind> {
303        match self.parser.peek_token().token {
304            Token::EOF | Token::SemiColon => Ok(ShowKind::All),
305            Token::Word(w) => match w.keyword {
306                Keyword::LIKE => {
307                    self.parser.next_token();
308                    Ok(ShowKind::Like(
309                        self.parser.parse_identifier().with_context(|_| {
310                            error::UnexpectedSnafu {
311                                expected: "LIKE",
312                                actual: self.peek_token_as_string(),
313                            }
314                        })?,
315                    ))
316                }
317                Keyword::WHERE => {
318                    self.parser.next_token();
319                    Ok(ShowKind::Where(self.parser.parse_expr().with_context(
320                        |_| error::UnexpectedSnafu {
321                            expected: "some valid expression",
322                            actual: self.peek_token_as_string(),
323                        },
324                    )?))
325                }
326                _ => self.unsupported(self.peek_token_as_string()),
327            },
328            _ => self.unsupported(self.peek_token_as_string()),
329        }
330    }
331
332    fn parse_show_index(&mut self) -> Result<Statement> {
333        let table = match self.parser.peek_token().token {
334            // SHOW INDEX {in | FROM} TABLE
335            Token::Word(w) if matches!(w.keyword, Keyword::IN | Keyword::FROM) => {
336                self.parse_show_table_name()?
337            }
338            _ => {
339                return error::UnexpectedTokenSnafu {
340                    expected: "{FROM | IN} table",
341                    actual: self.peek_token_as_string(),
342                }
343                .fail();
344            }
345        };
346
347        let database = match self.parser.peek_token().token {
348            Token::EOF | Token::SemiColon => {
349                return Ok(Statement::ShowIndex(ShowIndex {
350                    kind: ShowKind::All,
351                    table,
352                    database: None,
353                }));
354            }
355
356            // SHOW INDEX {In | FROM} TABLE {In | FROM} DATABASE
357            Token::Word(w) => match w.keyword {
358                Keyword::IN | Keyword::FROM => self.parse_db_name()?,
359
360                _ => None,
361            },
362            _ => None,
363        };
364
365        let kind = match self.parser.peek_token().token {
366            Token::EOF | Token::SemiColon => ShowKind::All,
367            // SHOW INDEX [WHERE] [EXPR]
368            Token::Word(w) => match w.keyword {
369                Keyword::WHERE => {
370                    self.parser.next_token();
371                    ShowKind::Where(self.parser.parse_expr().with_context(|_| {
372                        error::UnexpectedSnafu {
373                            expected: "some valid expression",
374                            actual: self.peek_token_as_string(),
375                        }
376                    })?)
377                }
378                _ => return self.unsupported(self.peek_token_as_string()),
379            },
380            _ => return self.unsupported(self.peek_token_as_string()),
381        };
382
383        Ok(Statement::ShowIndex(ShowIndex {
384            kind,
385            database,
386            table,
387        }))
388    }
389
390    fn parse_show_regions(&mut self) -> Result<Statement> {
391        let table = match self.parser.peek_token().token {
392            // SHOW REGION {in | FROM} TABLE
393            Token::Word(w) if matches!(w.keyword, Keyword::IN | Keyword::FROM) => {
394                self.parse_show_table_name()?
395            }
396            _ => {
397                return error::UnexpectedTokenSnafu {
398                    expected: "{FROM | IN} table",
399                    actual: self.peek_token_as_string(),
400                }
401                .fail();
402            }
403        };
404
405        let database = match self.parser.peek_token().token {
406            Token::EOF | Token::SemiColon => {
407                return Ok(Statement::ShowRegion(ShowRegion {
408                    kind: ShowKind::All,
409                    table,
410                    database: None,
411                }));
412            }
413
414            // SHOW REGION {In | FROM} TABLE {In | FROM} DATABASE
415            Token::Word(w) => match w.keyword {
416                Keyword::IN | Keyword::FROM => self.parse_db_name()?,
417
418                _ => None,
419            },
420            _ => None,
421        };
422
423        let kind = match self.parser.peek_token().token {
424            Token::EOF | Token::SemiColon => ShowKind::All,
425            // SHOW REGION [WHERE] [EXPR]
426            Token::Word(w) => match w.keyword {
427                Keyword::WHERE => {
428                    self.parser.next_token();
429                    ShowKind::Where(self.parser.parse_expr().with_context(|_| {
430                        error::UnexpectedSnafu {
431                            expected: "some valid expression",
432                            actual: self.peek_token_as_string(),
433                        }
434                    })?)
435                }
436                _ => return self.unsupported(self.peek_token_as_string()),
437            },
438            _ => return self.unsupported(self.peek_token_as_string()),
439        };
440
441        Ok(Statement::ShowRegion(ShowRegion {
442            kind,
443            database,
444            table,
445        }))
446    }
447
448    fn parse_show_tables(&mut self, full: bool) -> Result<Statement> {
449        let database = match self.parser.peek_token().token {
450            Token::EOF | Token::SemiColon => {
451                return Ok(Statement::ShowTables(ShowTables {
452                    kind: ShowKind::All,
453                    database: None,
454                    full,
455                }));
456            }
457
458            // SHOW TABLES [in | FROM] [DATABASE]
459            Token::Word(w) => match w.keyword {
460                Keyword::IN | Keyword::FROM => self.parse_db_name()?,
461
462                _ => None,
463            },
464            _ => None,
465        };
466
467        let kind = self.parse_show_kind()?;
468
469        Ok(Statement::ShowTables(ShowTables {
470            kind,
471            database,
472            full,
473        }))
474    }
475
476    fn parse_show_table_status(&mut self) -> Result<Statement> {
477        let database = match self.parser.peek_token().token {
478            Token::EOF | Token::SemiColon => {
479                return Ok(Statement::ShowTableStatus(ShowTableStatus {
480                    kind: ShowKind::All,
481                    database: None,
482                }));
483            }
484
485            // SHOW TABLE STATUS [in | FROM] [DATABASE]
486            Token::Word(w) => match w.keyword {
487                Keyword::IN | Keyword::FROM => self.parse_db_name()?,
488
489                _ => None,
490            },
491            _ => None,
492        };
493
494        let kind = self.parse_show_kind()?;
495
496        Ok(Statement::ShowTableStatus(ShowTableStatus {
497            kind,
498            database,
499        }))
500    }
501
502    /// Parses `SHOW DATABASES` statement.
503    pub fn parse_show_databases(&mut self, full: bool) -> Result<Statement> {
504        let tok = self.parser.next_token().token;
505        match &tok {
506            Token::EOF | Token::SemiColon => Ok(Statement::ShowDatabases(ShowDatabases::new(
507                ShowKind::All,
508                full,
509            ))),
510            Token::Word(w) => match w.keyword {
511                Keyword::LIKE => Ok(Statement::ShowDatabases(ShowDatabases::new(
512                    ShowKind::Like(self.parser.parse_identifier().with_context(|_| {
513                        error::UnexpectedSnafu {
514                            expected: "LIKE",
515                            actual: tok.to_string(),
516                        }
517                    })?),
518                    full,
519                ))),
520                Keyword::WHERE => Ok(Statement::ShowDatabases(ShowDatabases::new(
521                    ShowKind::Where(self.parser.parse_expr().with_context(|_| {
522                        error::UnexpectedSnafu {
523                            expected: "some valid expression",
524                            actual: self.peek_token_as_string(),
525                        }
526                    })?),
527                    full,
528                ))),
529                _ => self.unsupported(self.peek_token_as_string()),
530            },
531            _ => self.unsupported(self.peek_token_as_string()),
532        }
533    }
534
535    fn parse_show_views(&mut self) -> Result<Statement> {
536        let database = match self.parser.peek_token().token {
537            Token::EOF | Token::SemiColon => {
538                return Ok(Statement::ShowViews(ShowViews {
539                    kind: ShowKind::All,
540                    database: None,
541                }));
542            }
543
544            // SHOW VIEWS [in | FROM] [DATABASE]
545            Token::Word(w) => match w.keyword {
546                Keyword::IN | Keyword::FROM => self.parse_db_name()?,
547                _ => None,
548            },
549            _ => None,
550        };
551
552        let kind = self.parse_show_kind()?;
553
554        Ok(Statement::ShowViews(ShowViews { kind, database }))
555    }
556
557    fn parse_show_flows(&mut self) -> Result<Statement> {
558        let database = match self.parser.peek_token().token {
559            Token::EOF | Token::SemiColon => {
560                return Ok(Statement::ShowFlows(ShowFlows {
561                    kind: ShowKind::All,
562                    database: None,
563                }));
564            }
565
566            // SHOW FLOWS [in | FROM] [DATABASE]
567            Token::Word(w) => match w.keyword {
568                Keyword::IN | Keyword::FROM => self.parse_db_name()?,
569                _ => None,
570            },
571            _ => None,
572        };
573
574        let kind = self.parse_show_kind()?;
575
576        Ok(Statement::ShowFlows(ShowFlows { kind, database }))
577    }
578
579    fn parse_show_processlist(&mut self, full: bool) -> Result<Statement> {
580        match self.parser.next_token().token {
581            Token::EOF | Token::SemiColon => {
582                Ok(Statement::ShowProcesslist(ShowProcessList { full }))
583            }
584            _ => self.unsupported(self.peek_token_as_string()),
585        }
586    }
587}
588
589#[cfg(test)]
590mod tests {
591    use std::assert_matches::assert_matches;
592
593    use sqlparser::ast::{Ident, ObjectName};
594
595    use super::*;
596    use crate::dialect::GreptimeDbDialect;
597    use crate::parser::ParseOptions;
598    use crate::statements::show::ShowDatabases;
599
600    #[test]
601    pub fn test_show_database_all() {
602        let sql = "SHOW DATABASES";
603        let result =
604            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
605        let stmts = result.unwrap();
606        assert_eq!(1, stmts.len());
607
608        assert_matches!(
609            &stmts[0],
610            Statement::ShowDatabases(ShowDatabases {
611                kind: ShowKind::All,
612                full: false,
613            })
614        );
615    }
616
617    #[test]
618    pub fn test_show_full_databases() {
619        let sql = "SHOW FULL DATABASES";
620        let result =
621            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
622        let stmts = result.unwrap();
623        assert_eq!(1, stmts.len());
624
625        assert_matches!(
626            &stmts[0],
627            Statement::ShowDatabases(ShowDatabases {
628                kind: ShowKind::All,
629                full: true,
630            })
631        );
632
633        let sql = "SHOW FULL DATABASES LIKE 'test%'";
634        let result =
635            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
636        let stmts = result.unwrap();
637        assert_eq!(1, stmts.len());
638
639        assert_matches!(
640            &stmts[0],
641            Statement::ShowDatabases(ShowDatabases {
642                kind: ShowKind::Like(_),
643                full: true,
644            })
645        );
646    }
647
648    #[test]
649    pub fn test_show_database_like() {
650        let sql = "SHOW DATABASES LIKE test_database";
651        let result =
652            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
653        let stmts = result.unwrap();
654        assert_eq!(1, stmts.len());
655
656        assert_matches!(
657            &stmts[0],
658            Statement::ShowDatabases(ShowDatabases {
659                kind: ShowKind::Like(sqlparser::ast::Ident {
660                    value: _,
661                    quote_style: None,
662                    span: _,
663                }),
664                ..
665            })
666        );
667    }
668
669    #[test]
670    pub fn test_show_database_where() {
671        let sql = "SHOW DATABASES WHERE Database LIKE '%whatever1%' OR Database LIKE '%whatever2%'";
672        let result =
673            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
674        let stmts = result.unwrap();
675        assert_eq!(1, stmts.len());
676
677        assert_matches!(
678            &stmts[0],
679            Statement::ShowDatabases(ShowDatabases {
680                kind: ShowKind::Where(sqlparser::ast::Expr::BinaryOp {
681                    left: _,
682                    right: _,
683                    op: sqlparser::ast::BinaryOperator::Or,
684                }),
685                ..
686            })
687        );
688    }
689
690    #[test]
691    pub fn test_show_tables_all() {
692        let sql = "SHOW TABLES";
693        let result =
694            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
695        let stmts = result.unwrap();
696        assert_eq!(1, stmts.len());
697
698        assert_matches!(
699            &stmts[0],
700            Statement::ShowTables(ShowTables {
701                kind: ShowKind::All,
702                database: None,
703                full: false
704            })
705        );
706    }
707
708    #[test]
709    pub fn test_show_tables_like() {
710        let sql = "SHOW TABLES LIKE test_table";
711        let result =
712            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
713        let stmts = result.unwrap();
714        assert_eq!(1, stmts.len());
715
716        assert_matches!(
717            &stmts[0],
718            Statement::ShowTables(ShowTables {
719                kind: ShowKind::Like(sqlparser::ast::Ident {
720                    value: _,
721                    quote_style: None,
722                    span: _,
723                }),
724                database: None,
725                full: false
726            })
727        );
728
729        let sql = "SHOW TABLES in test_db LIKE test_table";
730        let result =
731            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
732        let stmts = result.unwrap();
733        assert_eq!(1, stmts.len());
734
735        assert_matches!(
736            &stmts[0],
737            Statement::ShowTables(ShowTables {
738                kind: ShowKind::Like(sqlparser::ast::Ident {
739                    value: _,
740                    quote_style: None,
741                    span: _,
742                }),
743                database: Some(_),
744                full: false
745            })
746        );
747    }
748
749    #[test]
750    pub fn test_show_tables_where() {
751        let sql = "SHOW TABLES where name like test_table";
752        let result =
753            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
754        let stmts = result.unwrap();
755        assert_eq!(1, stmts.len());
756
757        assert_matches!(
758            &stmts[0],
759            Statement::ShowTables(ShowTables {
760                kind: ShowKind::Where(sqlparser::ast::Expr::Like { .. }),
761                database: None,
762                full: false
763            })
764        );
765
766        let sql = "SHOW TABLES in test_db where name LIKE test_table";
767        let result =
768            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
769        let stmts = result.unwrap();
770        assert_eq!(1, stmts.len());
771
772        assert_matches!(
773            &stmts[0],
774            Statement::ShowTables(ShowTables {
775                kind: ShowKind::Where(sqlparser::ast::Expr::Like { .. }),
776                database: Some(_),
777                full: false
778            })
779        );
780    }
781
782    #[test]
783    pub fn test_show_full_tables() {
784        let sql = "SHOW FULL TABLES";
785        let stmts =
786            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
787                .unwrap();
788        assert_eq!(1, stmts.len());
789        assert_matches!(&stmts[0], Statement::ShowTables { .. });
790        match &stmts[0] {
791            Statement::ShowTables(show) => {
792                assert!(show.full);
793            }
794            _ => {
795                unreachable!();
796            }
797        }
798    }
799
800    #[test]
801    pub fn test_show_full_tables_where() {
802        let sql = "SHOW FULL TABLES IN test_db WHERE Tables LIKE test_table";
803        let stmts =
804            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
805                .unwrap();
806        assert_eq!(1, stmts.len());
807
808        assert_matches!(
809            &stmts[0],
810            Statement::ShowTables(ShowTables {
811                kind: ShowKind::Where(sqlparser::ast::Expr::Like { .. }),
812                database: Some(_),
813                full: true
814            })
815        );
816    }
817
818    #[test]
819    pub fn test_show_full_tables_like() {
820        let sql = "SHOW FULL TABLES LIKE test_table";
821        let result =
822            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
823        let stmts = result.unwrap();
824        assert_eq!(1, stmts.len());
825
826        assert_matches!(
827            &stmts[0],
828            Statement::ShowTables(ShowTables {
829                kind: ShowKind::Like(sqlparser::ast::Ident {
830                    value: _,
831                    quote_style: None,
832                    span: _,
833                }),
834                database: None,
835                full: true
836            })
837        );
838
839        let sql = "SHOW FULL TABLES in test_db LIKE test_table";
840        let result =
841            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
842        let stmts = result.unwrap();
843        assert_eq!(1, stmts.len());
844
845        assert_matches!(
846            &stmts[0],
847            Statement::ShowTables(ShowTables {
848                kind: ShowKind::Like(sqlparser::ast::Ident {
849                    value: _,
850                    quote_style: None,
851                    span: _,
852                }),
853                database: Some(_),
854                full: true
855            })
856        );
857    }
858
859    #[test]
860    pub fn test_show_variables() {
861        let sql = "SHOW VARIABLES system_time_zone";
862        let result =
863            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
864        let stmts = result.unwrap();
865        assert_eq!(1, stmts.len());
866        assert_eq!(
867            stmts[0],
868            Statement::ShowVariables(ShowVariables {
869                variable: ObjectName(vec![Ident::new("system_time_zone")]),
870            })
871        );
872    }
873
874    #[test]
875    pub fn test_show_columns() {
876        let sql = "SHOW COLUMNS";
877        let result =
878            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
879        let error = result.unwrap_err();
880        assert_eq!("Unexpected token while parsing SQL statement, expected: '{FROM | IN} table', found: EOF", error.to_string());
881
882        let sql = "SHOW COLUMNS from test";
883        let result =
884            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
885        let stmts = result.unwrap();
886        assert_eq!(1, stmts.len());
887        assert!(matches!(&stmts[0],
888                         Statement::ShowColumns(ShowColumns {
889                             table,
890                             database,
891                             full,
892                             ..
893
894                         }) if table == "test" && database.is_none() && !full));
895
896        let sql = "SHOW FULL COLUMNS from test from public";
897        let result =
898            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
899        let stmts = result.unwrap();
900        assert_eq!(1, stmts.len());
901        assert!(matches!(&stmts[0],
902                         Statement::ShowColumns(ShowColumns {
903                             table,
904                             database: Some(database),
905                             full,
906                             ..
907                         }) if table == "test" && database == "public" && *full));
908
909        let sql = "SHOW COLUMNS from test like 'disk%'";
910        let result =
911            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
912        let stmts = result.unwrap();
913        assert_eq!(1, stmts.len());
914        assert!(matches!(&stmts[0],
915                         Statement::ShowColumns(ShowColumns {
916                             table,
917                             kind: ShowKind::Like(ident),
918                             ..
919                         }) if table == "test" && ident.to_string() == "'disk%'"));
920
921        let sql = "SHOW COLUMNS from test where Field = 'disk'";
922        let result =
923            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
924        let stmts = result.unwrap();
925        assert_eq!(1, stmts.len());
926        assert!(matches!(&stmts[0],
927                         Statement::ShowColumns(ShowColumns {
928                             table,
929                             kind: ShowKind::Where(expr),
930                             ..
931                         }) if table == "test" && expr.to_string() == "Field = 'disk'"));
932    }
933
934    #[test]
935    pub fn test_show_index() {
936        let sql = "SHOW INDEX";
937        let result =
938            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
939        let error = result.unwrap_err();
940        assert_eq!("Unexpected token while parsing SQL statement, expected: '{FROM | IN} table', found: EOF", error.to_string());
941
942        let sql = "SHOW INDEX from test";
943        let result =
944            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
945        let stmts = result.unwrap();
946        assert_eq!(1, stmts.len());
947        assert!(matches!(&stmts[0],
948                         Statement::ShowIndex(ShowIndex {
949                             table,
950                             database,
951                             ..
952
953                         }) if table == "test" && database.is_none()));
954
955        let sql = "SHOW INDEX from test from public";
956        let result =
957            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
958        let stmts = result.unwrap();
959        assert_eq!(1, stmts.len());
960        assert!(matches!(&stmts[0],
961                         Statement::ShowIndex(ShowIndex {
962                             table,
963                             database: Some(database),
964                             ..
965                         }) if table == "test" && database == "public"));
966
967        // SHOW INDEX deosn't support like
968        let sql = "SHOW INDEX from test like 'disk%'";
969        let result =
970            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
971        let error = result.unwrap_err();
972        assert_eq!(
973            "SQL statement is not supported, keyword: like",
974            error.to_string()
975        );
976
977        let sql = "SHOW INDEX from test where Field = 'disk'";
978        let result =
979            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
980        let stmts = result.unwrap();
981        assert_eq!(1, stmts.len());
982        assert!(matches!(&stmts[0],
983                         Statement::ShowIndex(ShowIndex {
984                             table,
985                             kind: ShowKind::Where(expr),
986                             ..
987                         }) if table == "test" && expr.to_string() == "Field = 'disk'"));
988    }
989
990    #[test]
991    fn test_show_region() {
992        let sql = "SHOW REGION";
993        let result =
994            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
995        let error = result.unwrap_err();
996        assert_eq!("Unexpected token while parsing SQL statement, expected: '{FROM | IN} table', found: EOF", error.to_string());
997
998        let sql = "SHOW REGION from test";
999        let result =
1000            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1001        let stmts = result.unwrap();
1002        assert_eq!(1, stmts.len());
1003        assert!(matches!(&stmts[0],
1004                         Statement::ShowRegion(ShowRegion {
1005                             table,
1006                             database,
1007                             ..
1008
1009                         }) if table == "test" && database.is_none()));
1010
1011        let sql = "SHOW REGION from test from public";
1012        let result =
1013            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1014        let stmts = result.unwrap();
1015        assert_eq!(1, stmts.len());
1016        assert!(matches!(&stmts[0],
1017                         Statement::ShowRegion(ShowRegion {
1018                             table,
1019                             database: Some(database),
1020                             ..
1021                         }) if table == "test" && database == "public"));
1022
1023        // SHOW REGION deosn't support like
1024        let sql = "SHOW REGION from test like 'disk%'";
1025        let result =
1026            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1027        let error = result.unwrap_err();
1028        assert_eq!(
1029            "SQL statement is not supported, keyword: like",
1030            error.to_string()
1031        );
1032
1033        let sql = "SHOW REGION from test where Field = 'disk'";
1034        let result =
1035            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1036        let stmts = result.unwrap();
1037        assert_eq!(1, stmts.len());
1038        assert!(matches!(&stmts[0],
1039                         Statement::ShowRegion(ShowRegion {
1040                             table,
1041                             kind: ShowKind::Where(expr),
1042                             ..
1043                         }) if table == "test" && expr.to_string() == "Field = 'disk'"));
1044    }
1045
1046    #[test]
1047    fn parse_show_collation() {
1048        let sql = "SHOW COLLATION";
1049        let result =
1050            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1051        assert!(matches!(
1052            result.unwrap()[0],
1053            Statement::ShowCollation(ShowKind::All)
1054        ));
1055
1056        let sql = "SHOW COLLATION WHERE Charset = 'latin1'";
1057        let result =
1058            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1059        assert!(matches!(
1060            result.unwrap()[0],
1061            Statement::ShowCollation(ShowKind::Where(_))
1062        ));
1063
1064        let sql = "SHOW COLLATION LIKE 'latin1'";
1065        let result =
1066            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1067        assert!(matches!(
1068            result.unwrap()[0],
1069            Statement::ShowCollation(ShowKind::Like(_))
1070        ));
1071    }
1072
1073    #[test]
1074    fn parse_show_charset() {
1075        let sql = "SHOW CHARSET";
1076        let result =
1077            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1078        assert!(matches!(
1079            result.unwrap()[0],
1080            Statement::ShowCharset(ShowKind::All)
1081        ));
1082
1083        let sql = "SHOW CHARACTER SET";
1084        let result =
1085            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1086        assert!(matches!(
1087            result.unwrap()[0],
1088            Statement::ShowCharset(ShowKind::All)
1089        ));
1090
1091        let sql = "SHOW CHARSET WHERE Charset = 'latin1'";
1092        let result =
1093            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1094        assert!(matches!(
1095            result.unwrap()[0],
1096            Statement::ShowCharset(ShowKind::Where(_))
1097        ));
1098
1099        let sql = "SHOW CHARACTER SET LIKE 'latin1'";
1100        let result =
1101            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1102        assert!(matches!(
1103            result.unwrap()[0],
1104            Statement::ShowCharset(ShowKind::Like(_))
1105        ));
1106    }
1107
1108    fn parse_show_table_status(sql: &str) -> ShowTableStatus {
1109        let result =
1110            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1111        let mut stmts = result.unwrap();
1112        assert_eq!(1, stmts.len());
1113
1114        match stmts.remove(0) {
1115            Statement::ShowTableStatus(stmt) => stmt,
1116            _ => panic!("Failed to parse show table status"),
1117        }
1118    }
1119
1120    #[test]
1121    pub fn test_show_table_status() {
1122        let sql = "SHOW TABLE STATUS";
1123        let stmt = parse_show_table_status(sql);
1124        assert!(stmt.database.is_none());
1125        assert_eq!(sql, stmt.to_string());
1126
1127        let sql = "SHOW TABLE STATUS IN test";
1128        let stmt = parse_show_table_status(sql);
1129        assert_eq!("test", stmt.database.as_ref().unwrap());
1130        assert_eq!(sql, stmt.to_string());
1131
1132        let sql = "SHOW TABLE STATUS LIKE '%monitor'";
1133        let stmt = parse_show_table_status(sql);
1134        assert!(stmt.database.is_none());
1135        assert!(matches!(stmt.kind, ShowKind::Like(_)));
1136        assert_eq!(sql, stmt.to_string());
1137
1138        let sql = "SHOW TABLE STATUS IN test WHERE Name = 'monitor'";
1139        let stmt = parse_show_table_status(sql);
1140        assert_eq!("test", stmt.database.as_ref().unwrap());
1141        assert!(matches!(stmt.kind, ShowKind::Where(_)));
1142        assert_eq!(sql, stmt.to_string());
1143    }
1144
1145    #[test]
1146    pub fn test_show_create_view() {
1147        let sql = "SHOW CREATE VIEW test";
1148        let result =
1149            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1150        let stmts = result.unwrap();
1151        assert_eq!(1, stmts.len());
1152        assert_eq!(
1153            stmts[0],
1154            Statement::ShowCreateView(ShowCreateView {
1155                view_name: ObjectName(vec![Ident::new("test")]),
1156            })
1157        );
1158        assert_eq!(sql, stmts[0].to_string());
1159    }
1160
1161    #[test]
1162    pub fn test_show_views() {
1163        let sql = "SHOW VIEWS";
1164        let result =
1165            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1166        let stmts = result.unwrap();
1167        assert_eq!(1, stmts.len());
1168        assert_eq!(
1169            stmts[0],
1170            Statement::ShowViews(ShowViews {
1171                kind: ShowKind::All,
1172                database: None,
1173            })
1174        );
1175        assert_eq!(sql, stmts[0].to_string());
1176    }
1177
1178    #[test]
1179    pub fn test_show_views_in_db() {
1180        let sql = "SHOW VIEWS IN d1";
1181        let result =
1182            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1183        let stmts = result.unwrap();
1184        assert_eq!(1, stmts.len());
1185        assert_eq!(
1186            stmts[0],
1187            Statement::ShowViews(ShowViews {
1188                kind: ShowKind::All,
1189                database: Some("d1".to_string()),
1190            })
1191        );
1192        assert_eq!(sql, stmts[0].to_string());
1193    }
1194
1195    #[test]
1196    pub fn test_show_flows() {
1197        let sql = "SHOW FLOWS";
1198        let result =
1199            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1200        let stmts = result.unwrap();
1201        assert_eq!(1, stmts.len());
1202        assert_eq!(
1203            stmts[0],
1204            Statement::ShowFlows(ShowFlows {
1205                kind: ShowKind::All,
1206                database: None,
1207            })
1208        );
1209        assert_eq!(sql, stmts[0].to_string());
1210    }
1211
1212    #[test]
1213    pub fn test_show_flows_in_db() {
1214        let sql = "SHOW FLOWS IN d1";
1215        let result =
1216            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1217        let stmts = result.unwrap();
1218        assert_eq!(1, stmts.len());
1219        assert_eq!(
1220            stmts[0],
1221            Statement::ShowFlows(ShowFlows {
1222                kind: ShowKind::All,
1223                database: Some("d1".to_string()),
1224            })
1225        );
1226        assert_eq!(sql, stmts[0].to_string());
1227    }
1228
1229    #[test]
1230    pub fn test_show_processlist() {
1231        let sql = "SHOW PROCESSLIST";
1232        let result =
1233            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1234        let stmts = result.unwrap();
1235        assert_eq!(1, stmts.len());
1236        assert_eq!(
1237            stmts[0],
1238            Statement::ShowProcesslist(ShowProcessList { full: false })
1239        );
1240        assert_eq!(sql, stmts[0].to_string());
1241
1242        let sql = "SHOW FULL PROCESSLIST";
1243        let result =
1244            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1245        let stmts = result.unwrap();
1246        assert_eq!(1, stmts.len());
1247        assert_eq!(
1248            stmts[0],
1249            Statement::ShowProcesslist(ShowProcessList { full: true })
1250        );
1251        assert_eq!(sql, stmts[0].to_string());
1252    }
1253}