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::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!("Unexpected token while parsing SQL statement, expected: '{FROM | IN} table', found: EOF", error.to_string());
880
881        let sql = "SHOW COLUMNS from test";
882        let result =
883            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
884        let stmts = result.unwrap();
885        assert_eq!(1, stmts.len());
886        assert!(matches!(&stmts[0],
887                         Statement::ShowColumns(ShowColumns {
888                             table,
889                             database,
890                             full,
891                             ..
892
893                         }) if table == "test" && database.is_none() && !full));
894
895        let sql = "SHOW FULL COLUMNS from test from public";
896        let result =
897            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
898        let stmts = result.unwrap();
899        assert_eq!(1, stmts.len());
900        assert!(matches!(&stmts[0],
901                         Statement::ShowColumns(ShowColumns {
902                             table,
903                             database: Some(database),
904                             full,
905                             ..
906                         }) if table == "test" && database == "public" && *full));
907
908        let sql = "SHOW COLUMNS from test like '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::Like(ident),
917                             ..
918                         }) if table == "test" && ident.to_string() == "'disk%'"));
919
920        let sql = "SHOW COLUMNS from test where Field = 'disk'";
921        let result =
922            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
923        let stmts = result.unwrap();
924        assert_eq!(1, stmts.len());
925        assert!(matches!(&stmts[0],
926                         Statement::ShowColumns(ShowColumns {
927                             table,
928                             kind: ShowKind::Where(expr),
929                             ..
930                         }) if table == "test" && expr.to_string() == "Field = 'disk'"));
931    }
932
933    #[test]
934    pub fn test_show_index() {
935        let sql = "SHOW INDEX";
936        let result =
937            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
938        let error = result.unwrap_err();
939        assert_eq!("Unexpected token while parsing SQL statement, expected: '{FROM | IN} table', found: EOF", error.to_string());
940
941        let sql = "SHOW INDEX from test";
942        let result =
943            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
944        let stmts = result.unwrap();
945        assert_eq!(1, stmts.len());
946        assert!(matches!(&stmts[0],
947                         Statement::ShowIndex(ShowIndex {
948                             table,
949                             database,
950                             ..
951
952                         }) if table == "test" && database.is_none()));
953
954        let sql = "SHOW INDEX from test from public";
955        let result =
956            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
957        let stmts = result.unwrap();
958        assert_eq!(1, stmts.len());
959        assert!(matches!(&stmts[0],
960                         Statement::ShowIndex(ShowIndex {
961                             table,
962                             database: Some(database),
963                             ..
964                         }) if table == "test" && database == "public"));
965
966        // SHOW INDEX deosn't support like
967        let sql = "SHOW INDEX from test like 'disk%'";
968        let result =
969            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
970        let error = result.unwrap_err();
971        assert_eq!(
972            "SQL statement is not supported, keyword: like",
973            error.to_string()
974        );
975
976        let sql = "SHOW INDEX from test where Field = 'disk'";
977        let result =
978            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
979        let stmts = result.unwrap();
980        assert_eq!(1, stmts.len());
981        assert!(matches!(&stmts[0],
982                         Statement::ShowIndex(ShowIndex {
983                             table,
984                             kind: ShowKind::Where(expr),
985                             ..
986                         }) if table == "test" && expr.to_string() == "Field = 'disk'"));
987    }
988
989    #[test]
990    fn test_show_region() {
991        let sql = "SHOW REGION";
992        let result =
993            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
994        let error = result.unwrap_err();
995        assert_eq!("Unexpected token while parsing SQL statement, expected: '{FROM | IN} table', found: EOF", error.to_string());
996
997        let sql = "SHOW REGION from test";
998        let result =
999            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1000        let stmts = result.unwrap();
1001        assert_eq!(1, stmts.len());
1002        assert!(matches!(&stmts[0],
1003                         Statement::ShowRegion(ShowRegion {
1004                             table,
1005                             database,
1006                             ..
1007
1008                         }) if table == "test" && database.is_none()));
1009
1010        let sql = "SHOW REGION from test from public";
1011        let result =
1012            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1013        let stmts = result.unwrap();
1014        assert_eq!(1, stmts.len());
1015        assert!(matches!(&stmts[0],
1016                         Statement::ShowRegion(ShowRegion {
1017                             table,
1018                             database: Some(database),
1019                             ..
1020                         }) if table == "test" && database == "public"));
1021
1022        // SHOW REGION deosn't support like
1023        let sql = "SHOW REGION from test like 'disk%'";
1024        let result =
1025            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1026        let error = result.unwrap_err();
1027        assert_eq!(
1028            "SQL statement is not supported, keyword: like",
1029            error.to_string()
1030        );
1031
1032        let sql = "SHOW REGION from test where Field = 'disk'";
1033        let result =
1034            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1035        let stmts = result.unwrap();
1036        assert_eq!(1, stmts.len());
1037        assert!(matches!(&stmts[0],
1038                         Statement::ShowRegion(ShowRegion {
1039                             table,
1040                             kind: ShowKind::Where(expr),
1041                             ..
1042                         }) if table == "test" && expr.to_string() == "Field = 'disk'"));
1043    }
1044
1045    #[test]
1046    fn parse_show_collation() {
1047        let sql = "SHOW COLLATION";
1048        let result =
1049            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1050        assert!(matches!(
1051            result.unwrap()[0],
1052            Statement::ShowCollation(ShowKind::All)
1053        ));
1054
1055        let sql = "SHOW COLLATION WHERE Charset = 'latin1'";
1056        let result =
1057            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1058        assert!(matches!(
1059            result.unwrap()[0],
1060            Statement::ShowCollation(ShowKind::Where(_))
1061        ));
1062
1063        let sql = "SHOW COLLATION LIKE 'latin1'";
1064        let result =
1065            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1066        assert!(matches!(
1067            result.unwrap()[0],
1068            Statement::ShowCollation(ShowKind::Like(_))
1069        ));
1070    }
1071
1072    #[test]
1073    fn parse_show_charset() {
1074        let sql = "SHOW CHARSET";
1075        let result =
1076            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1077        assert!(matches!(
1078            result.unwrap()[0],
1079            Statement::ShowCharset(ShowKind::All)
1080        ));
1081
1082        let sql = "SHOW CHARACTER SET";
1083        let result =
1084            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1085        assert!(matches!(
1086            result.unwrap()[0],
1087            Statement::ShowCharset(ShowKind::All)
1088        ));
1089
1090        let sql = "SHOW CHARSET WHERE Charset = 'latin1'";
1091        let result =
1092            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1093        assert!(matches!(
1094            result.unwrap()[0],
1095            Statement::ShowCharset(ShowKind::Where(_))
1096        ));
1097
1098        let sql = "SHOW CHARACTER SET LIKE 'latin1'";
1099        let result =
1100            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1101        assert!(matches!(
1102            result.unwrap()[0],
1103            Statement::ShowCharset(ShowKind::Like(_))
1104        ));
1105    }
1106
1107    fn parse_show_table_status(sql: &str) -> ShowTableStatus {
1108        let result =
1109            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1110        let mut stmts = result.unwrap();
1111        assert_eq!(1, stmts.len());
1112
1113        match stmts.remove(0) {
1114            Statement::ShowTableStatus(stmt) => stmt,
1115            _ => panic!("Failed to parse show table status"),
1116        }
1117    }
1118
1119    #[test]
1120    pub fn test_show_table_status() {
1121        let sql = "SHOW TABLE STATUS";
1122        let stmt = parse_show_table_status(sql);
1123        assert!(stmt.database.is_none());
1124        assert_eq!(sql, stmt.to_string());
1125
1126        let sql = "SHOW TABLE STATUS IN test";
1127        let stmt = parse_show_table_status(sql);
1128        assert_eq!("test", stmt.database.as_ref().unwrap());
1129        assert_eq!(sql, stmt.to_string());
1130
1131        let sql = "SHOW TABLE STATUS LIKE '%monitor'";
1132        let stmt = parse_show_table_status(sql);
1133        assert!(stmt.database.is_none());
1134        assert!(matches!(stmt.kind, ShowKind::Like(_)));
1135        assert_eq!(sql, stmt.to_string());
1136
1137        let sql = "SHOW TABLE STATUS IN test WHERE Name = 'monitor'";
1138        let stmt = parse_show_table_status(sql);
1139        assert_eq!("test", stmt.database.as_ref().unwrap());
1140        assert!(matches!(stmt.kind, ShowKind::Where(_)));
1141        assert_eq!(sql, stmt.to_string());
1142    }
1143
1144    #[test]
1145    pub fn test_show_create_view() {
1146        let sql = "SHOW CREATE VIEW test";
1147        let result =
1148            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1149        let stmts = result.unwrap();
1150        assert_eq!(1, stmts.len());
1151        assert_eq!(
1152            stmts[0],
1153            Statement::ShowCreateView(ShowCreateView {
1154                view_name: ObjectName::from(vec![Ident::new("test")]),
1155            })
1156        );
1157        assert_eq!(sql, stmts[0].to_string());
1158    }
1159
1160    #[test]
1161    pub fn test_show_views() {
1162        let sql = "SHOW VIEWS";
1163        let result =
1164            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1165        let stmts = result.unwrap();
1166        assert_eq!(1, stmts.len());
1167        assert_eq!(
1168            stmts[0],
1169            Statement::ShowViews(ShowViews {
1170                kind: ShowKind::All,
1171                database: None,
1172            })
1173        );
1174        assert_eq!(sql, stmts[0].to_string());
1175    }
1176
1177    #[test]
1178    pub fn test_show_views_in_db() {
1179        let sql = "SHOW VIEWS IN d1";
1180        let result =
1181            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1182        let stmts = result.unwrap();
1183        assert_eq!(1, stmts.len());
1184        assert_eq!(
1185            stmts[0],
1186            Statement::ShowViews(ShowViews {
1187                kind: ShowKind::All,
1188                database: Some("d1".to_string()),
1189            })
1190        );
1191        assert_eq!(sql, stmts[0].to_string());
1192    }
1193
1194    #[test]
1195    pub fn test_show_flows() {
1196        let sql = "SHOW FLOWS";
1197        let result =
1198            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1199        let stmts = result.unwrap();
1200        assert_eq!(1, stmts.len());
1201        assert_eq!(
1202            stmts[0],
1203            Statement::ShowFlows(ShowFlows {
1204                kind: ShowKind::All,
1205                database: None,
1206            })
1207        );
1208        assert_eq!(sql, stmts[0].to_string());
1209    }
1210
1211    #[test]
1212    pub fn test_show_flows_in_db() {
1213        let sql = "SHOW FLOWS IN d1";
1214        let result =
1215            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1216        let stmts = result.unwrap();
1217        assert_eq!(1, stmts.len());
1218        assert_eq!(
1219            stmts[0],
1220            Statement::ShowFlows(ShowFlows {
1221                kind: ShowKind::All,
1222                database: Some("d1".to_string()),
1223            })
1224        );
1225        assert_eq!(sql, stmts[0].to_string());
1226    }
1227
1228    #[test]
1229    pub fn test_show_processlist() {
1230        let sql = "SHOW PROCESSLIST";
1231        let result =
1232            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1233        let stmts = result.unwrap();
1234        assert_eq!(1, stmts.len());
1235        assert_eq!(
1236            stmts[0],
1237            Statement::ShowProcesslist(ShowProcessList { full: false })
1238        );
1239        assert_eq!(sql, stmts[0].to_string());
1240
1241        let sql = "SHOW FULL PROCESSLIST";
1242        let result =
1243            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1244        let stmts = result.unwrap();
1245        assert_eq!(1, stmts.len());
1246        assert_eq!(
1247            stmts[0],
1248            Statement::ShowProcesslist(ShowProcessList { full: true })
1249        );
1250        assert_eq!(sql, stmts[0].to_string());
1251    }
1252}