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