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