From e2197eeca9ef2d51a26f29ac23c15515aa668a0f Mon Sep 17 00:00:00 2001 From: Yoav Cohen <59807311+yoavcloud@users.noreply.github.com> Date: Sun, 3 Nov 2024 14:07:06 +0100 Subject: [PATCH] Add support for SHOW DATABASES/SCHEMAS/TABLES/VIEWS in Hive (#1487) --- src/ast/mod.rs | 81 ++++++++++++++++++++++++++++++++++++++- src/keywords.rs | 3 ++ src/parser/mod.rs | 50 ++++++++++++++++++++++-- tests/sqlparser_common.rs | 21 ++++++++++ tests/sqlparser_mysql.rs | 9 ++++- 5 files changed, 157 insertions(+), 7 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 0573240a2..b2672552e 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -2782,12 +2782,29 @@ pub enum Statement { filter: Option, }, /// ```sql + /// SHOW DATABASES [LIKE 'pattern'] + /// ``` + ShowDatabases { filter: Option }, + /// ```sql + /// SHOW SCHEMAS [LIKE 'pattern'] + /// ``` + ShowSchemas { filter: Option }, + /// ```sql /// SHOW TABLES /// ``` - /// Note: this is a MySQL-specific statement. ShowTables { extended: bool, full: bool, + clause: Option, + db_name: Option, + filter: Option, + }, + /// ```sql + /// SHOW VIEWS + /// ``` + ShowViews { + materialized: bool, + clause: Option, db_name: Option, filter: Option, }, @@ -4363,9 +4380,24 @@ impl fmt::Display for Statement { } Ok(()) } + Statement::ShowDatabases { filter } => { + write!(f, "SHOW DATABASES")?; + if let Some(filter) = filter { + write!(f, " {filter}")?; + } + Ok(()) + } + Statement::ShowSchemas { filter } => { + write!(f, "SHOW SCHEMAS")?; + if let Some(filter) = filter { + write!(f, " {filter}")?; + } + Ok(()) + } Statement::ShowTables { extended, full, + clause: show_clause, db_name, filter, } => { @@ -4375,8 +4407,33 @@ impl fmt::Display for Statement { extended = if *extended { "EXTENDED " } else { "" }, full = if *full { "FULL " } else { "" }, )?; + if let Some(show_clause) = show_clause { + write!(f, " {show_clause}")?; + } + if let Some(db_name) = db_name { + write!(f, " {db_name}")?; + } + if let Some(filter) = filter { + write!(f, " {filter}")?; + } + Ok(()) + } + Statement::ShowViews { + materialized, + clause: show_clause, + db_name, + filter, + } => { + write!( + f, + "SHOW {}VIEWS", + if *materialized { "MATERIALIZED " } else { "" } + )?; + if let Some(show_clause) = show_clause { + write!(f, " {show_clause}")?; + } if let Some(db_name) = db_name { - write!(f, " FROM {db_name}")?; + write!(f, " {db_name}")?; } if let Some(filter) = filter { write!(f, " {filter}")?; @@ -6057,6 +6114,7 @@ pub enum ShowStatementFilter { Like(String), ILike(String), Where(Expr), + NoKeyword(String), } impl fmt::Display for ShowStatementFilter { @@ -6066,6 +6124,25 @@ impl fmt::Display for ShowStatementFilter { Like(pattern) => write!(f, "LIKE '{}'", value::escape_single_quote_string(pattern)), ILike(pattern) => write!(f, "ILIKE {}", value::escape_single_quote_string(pattern)), Where(expr) => write!(f, "WHERE {expr}"), + NoKeyword(pattern) => write!(f, "'{}'", value::escape_single_quote_string(pattern)), + } + } +} + +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] +pub enum ShowClause { + IN, + FROM, +} + +impl fmt::Display for ShowClause { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use ShowClause::*; + match self { + FROM => write!(f, "FROM"), + IN => write!(f, "IN"), } } } diff --git a/src/keywords.rs b/src/keywords.rs index 6182ae176..e98309681 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -217,6 +217,7 @@ define_keywords!( CYCLE, DATA, DATABASE, + DATABASES, DATA_RETENTION_TIME_IN_DAYS, DATE, DATE32, @@ -662,6 +663,7 @@ define_keywords!( SAFE_CAST, SAVEPOINT, SCHEMA, + SCHEMAS, SCOPE, SCROLL, SEARCH, @@ -822,6 +824,7 @@ define_keywords!( VERSION, VERSIONING, VIEW, + VIEWS, VIRTUAL, VOLATILE, WAREHOUSE, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index a9a5b1df4..c4b92ba4e 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -9579,6 +9579,10 @@ impl<'a> Parser<'a> { Ok(self.parse_show_columns(extended, full)?) } else if self.parse_keyword(Keyword::TABLES) { Ok(self.parse_show_tables(extended, full)?) + } else if self.parse_keywords(&[Keyword::MATERIALIZED, Keyword::VIEWS]) { + Ok(self.parse_show_views(true)?) + } else if self.parse_keyword(Keyword::VIEWS) { + Ok(self.parse_show_views(false)?) } else if self.parse_keyword(Keyword::FUNCTIONS) { Ok(self.parse_show_functions()?) } else if extended || full { @@ -9605,6 +9609,10 @@ impl<'a> Parser<'a> { session, global, }) + } else if self.parse_keyword(Keyword::DATABASES) { + self.parse_show_databases() + } else if self.parse_keyword(Keyword::SCHEMAS) { + self.parse_show_schemas() } else { Ok(Statement::ShowVariable { variable: self.parse_identifiers()?, @@ -9612,6 +9620,18 @@ impl<'a> Parser<'a> { } } + fn parse_show_databases(&mut self) -> Result { + Ok(Statement::ShowDatabases { + filter: self.parse_show_statement_filter()?, + }) + } + + fn parse_show_schemas(&mut self) -> Result { + Ok(Statement::ShowSchemas { + filter: self.parse_show_statement_filter()?, + }) + } + pub fn parse_show_create(&mut self) -> Result { let obj_type = match self.expect_one_of_keywords(&[ Keyword::TABLE, @@ -9667,14 +9687,31 @@ impl<'a> Parser<'a> { extended: bool, full: bool, ) -> Result { - let db_name = match self.parse_one_of_keywords(&[Keyword::FROM, Keyword::IN]) { - Some(_) => Some(self.parse_identifier(false)?), - None => None, + let (clause, db_name) = match self.parse_one_of_keywords(&[Keyword::FROM, Keyword::IN]) { + Some(Keyword::FROM) => (Some(ShowClause::FROM), Some(self.parse_identifier(false)?)), + Some(Keyword::IN) => (Some(ShowClause::IN), Some(self.parse_identifier(false)?)), + _ => (None, None), }; let filter = self.parse_show_statement_filter()?; Ok(Statement::ShowTables { extended, full, + clause, + db_name, + filter, + }) + } + + fn parse_show_views(&mut self, materialized: bool) -> Result { + let (clause, db_name) = match self.parse_one_of_keywords(&[Keyword::FROM, Keyword::IN]) { + Some(Keyword::FROM) => (Some(ShowClause::FROM), Some(self.parse_identifier(false)?)), + Some(Keyword::IN) => (Some(ShowClause::IN), Some(self.parse_identifier(false)?)), + _ => (None, None), + }; + let filter = self.parse_show_statement_filter()?; + Ok(Statement::ShowViews { + materialized, + clause, db_name, filter, }) @@ -9704,7 +9741,12 @@ impl<'a> Parser<'a> { } else if self.parse_keyword(Keyword::WHERE) { Ok(Some(ShowStatementFilter::Where(self.parse_expr()?))) } else { - Ok(None) + self.maybe_parse(|parser| -> Result { + parser.parse_literal_string() + })? + .map_or(Ok(None), |filter| { + Ok(Some(ShowStatementFilter::NoKeyword(filter))) + }) } } diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index a2eb5070d..4016e5a69 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -11378,3 +11378,24 @@ fn test_try_convert() { all_dialects_where(|d| d.supports_try_convert() && !d.convert_type_before_value()); dialects.verified_expr("TRY_CONVERT('foo', VARCHAR(MAX))"); } + +#[test] +fn test_show_dbs_schemas_tables_views() { + verified_stmt("SHOW DATABASES"); + verified_stmt("SHOW DATABASES LIKE '%abc'"); + verified_stmt("SHOW SCHEMAS"); + verified_stmt("SHOW SCHEMAS LIKE '%abc'"); + verified_stmt("SHOW TABLES"); + verified_stmt("SHOW TABLES IN db1"); + verified_stmt("SHOW TABLES IN db1 'abc'"); + verified_stmt("SHOW VIEWS"); + verified_stmt("SHOW VIEWS IN db1"); + verified_stmt("SHOW VIEWS IN db1 'abc'"); + verified_stmt("SHOW VIEWS FROM db1"); + verified_stmt("SHOW VIEWS FROM db1 'abc'"); + verified_stmt("SHOW MATERIALIZED VIEWS"); + verified_stmt("SHOW MATERIALIZED VIEWS IN db1"); + verified_stmt("SHOW MATERIALIZED VIEWS IN db1 'abc'"); + verified_stmt("SHOW MATERIALIZED VIEWS FROM db1"); + verified_stmt("SHOW MATERIALIZED VIEWS FROM db1 'abc'"); +} diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index db5b9ec8d..4b9354e85 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -329,6 +329,7 @@ fn parse_show_tables() { Statement::ShowTables { extended: false, full: false, + clause: None, db_name: None, filter: None, } @@ -338,6 +339,7 @@ fn parse_show_tables() { Statement::ShowTables { extended: false, full: false, + clause: Some(ShowClause::FROM), db_name: Some(Ident::new("mydb")), filter: None, } @@ -347,6 +349,7 @@ fn parse_show_tables() { Statement::ShowTables { extended: true, full: false, + clause: None, db_name: None, filter: None, } @@ -356,6 +359,7 @@ fn parse_show_tables() { Statement::ShowTables { extended: false, full: true, + clause: None, db_name: None, filter: None, } @@ -365,6 +369,7 @@ fn parse_show_tables() { Statement::ShowTables { extended: false, full: false, + clause: None, db_name: None, filter: Some(ShowStatementFilter::Like("pattern".into())), } @@ -374,13 +379,15 @@ fn parse_show_tables() { Statement::ShowTables { extended: false, full: false, + clause: None, db_name: None, filter: Some(ShowStatementFilter::Where( mysql_and_generic().verified_expr("1 = 2") )), } ); - mysql_and_generic().one_statement_parses_to("SHOW TABLES IN mydb", "SHOW TABLES FROM mydb"); + mysql_and_generic().verified_stmt("SHOW TABLES IN mydb"); + mysql_and_generic().verified_stmt("SHOW TABLES FROM mydb"); } #[test]