diff --git a/src/frontend/src/instance.rs b/src/frontend/src/instance.rs index 90f29c9382e9..e59eccc97202 100644 --- a/src/frontend/src/instance.rs +++ b/src/frontend/src/instance.rs @@ -506,6 +506,9 @@ pub fn check_permission( Statement::ShowIndex(stmt) => { validate_db_permission!(stmt, query_ctx); } + Statement::ShowViews(stmt) => { + validate_db_permission!(stmt, query_ctx); + } Statement::ShowStatus(_stmt) => {} Statement::DescribeTable(stmt) => { validate_param(stmt.name(), query_ctx)?; diff --git a/src/operator/src/statement.rs b/src/operator/src/statement.rs index bb7fe25cc189..348cd9beae2e 100644 --- a/src/operator/src/statement.rs +++ b/src/operator/src/statement.rs @@ -136,6 +136,8 @@ impl StatementExecutor { Statement::ShowCharset(kind) => self.show_charset(kind, query_ctx).await, + Statement::ShowViews(stmt) => self.show_views(stmt, query_ctx).await, + Statement::Copy(sql::statements::copy::Copy::CopyTable(stmt)) => { let req = to_copy_table_request(stmt, query_ctx.clone())?; match req.direction { diff --git a/src/operator/src/statement/show.rs b/src/operator/src/statement/show.rs index e16aec5ddb35..74d843a6aff7 100644 --- a/src/operator/src/statement/show.rs +++ b/src/operator/src/statement/show.rs @@ -24,7 +24,7 @@ use sql::ast::Ident; use sql::statements::create::Partitions; use sql::statements::show::{ ShowColumns, ShowCreateFlow, ShowCreateView, ShowDatabases, ShowIndex, ShowKind, - ShowTableStatus, ShowTables, ShowVariables, + ShowTableStatus, ShowTables, ShowVariables, ShowViews, }; use table::metadata::TableType; use table::table_name::TableName; @@ -152,6 +152,17 @@ impl StatementExecutor { .context(error::ExecuteStatementSnafu) } + #[tracing::instrument(skip_all)] + pub(super) async fn show_views( + &self, + stmt: ShowViews, + query_ctx: QueryContextRef, + ) -> Result { + query::sql::show_views(stmt, &self.query_engine, &self.catalog_manager, query_ctx) + .await + .context(ExecuteStatementSnafu) + } + #[tracing::instrument(skip_all)] pub async fn show_create_flow( &self, diff --git a/src/query/src/sql.rs b/src/query/src/sql.rs index 3693e2010ec3..ca9524b6877e 100644 --- a/src/query/src/sql.rs +++ b/src/query/src/sql.rs @@ -19,7 +19,7 @@ use std::sync::Arc; use catalog::information_schema::{ columns, key_column_usage, schemata, tables, CHARACTER_SETS, COLLATIONS, COLUMNS, - KEY_COLUMN_USAGE, SCHEMATA, TABLES, + KEY_COLUMN_USAGE, SCHEMATA, TABLES, VIEWS, }; use catalog::CatalogManagerRef; use common_catalog::consts::{ @@ -55,6 +55,7 @@ use sql::parser::ParserContext; use sql::statements::create::{CreateFlow, CreateView, Partitions}; use sql::statements::show::{ ShowColumns, ShowDatabases, ShowIndex, ShowKind, ShowTableStatus, ShowTables, ShowVariables, + ShowViews, }; use sql::statements::statement::Statement; use sqlparser::ast::ObjectName; @@ -69,6 +70,7 @@ use crate::QueryEngineRef; const SCHEMAS_COLUMN: &str = "Database"; const OPTIONS_COLUMN: &str = "Options"; const TABLES_COLUMN: &str = "Tables"; +const VIEWS_COLUMN: &str = "Views"; const FIELD_COLUMN: &str = "Field"; const TABLE_TYPE_COLUMN: &str = "Table_type"; const COLUMN_NAME_COLUMN: &str = "Column"; @@ -725,6 +727,42 @@ pub fn show_create_view( Ok(Output::new_with_record_batches(records)) } +/// Execute [`ShowViews`] statement and return the [`Output`] if success. +pub async fn show_views( + stmt: ShowViews, + query_engine: &QueryEngineRef, + catalog_manager: &CatalogManagerRef, + query_ctx: QueryContextRef, +) -> Result { + let schema_name = if let Some(database) = stmt.database { + database + } else { + query_ctx.current_schema() + }; + + let projects = vec![(tables::TABLE_NAME, VIEWS_COLUMN)]; + let filters = vec![ + col(tables::TABLE_SCHEMA).eq(lit(schema_name.clone())), + col(tables::TABLE_CATALOG).eq(lit(query_ctx.current_catalog())), + ]; + let like_field = Some(tables::TABLE_NAME); + let sort = vec![col(tables::TABLE_NAME).sort(true, true)]; + + query_from_information_schema_table( + query_engine, + catalog_manager, + query_ctx, + VIEWS, + vec![], + projects, + filters, + like_field, + sort, + stmt.kind, + ) + .await +} + pub fn show_create_flow( flow_name: ObjectName, flow_val: FlowInfoValue, diff --git a/src/sql/src/parsers/show_parser.rs b/src/sql/src/parsers/show_parser.rs index b8c6e415492b..41f507b44d86 100644 --- a/src/sql/src/parsers/show_parser.rs +++ b/src/sql/src/parsers/show_parser.rs @@ -22,7 +22,7 @@ use crate::error::{ use crate::parser::ParserContext; use crate::statements::show::{ ShowColumns, ShowCreateFlow, ShowCreateTable, ShowCreateView, ShowDatabases, ShowIndex, - ShowKind, ShowStatus, ShowTableStatus, ShowTables, ShowVariables, + ShowKind, ShowStatus, ShowTableStatus, ShowTables, ShowVariables, ShowViews, }; use crate::statements::statement::Statement; @@ -44,6 +44,8 @@ impl<'a> ParserContext<'a> { } else { self.unsupported(self.peek_token_as_string()) } + } else if self.consume_token("VIEWS") { + self.parse_show_views() } else if self.matches_keyword(Keyword::CHARSET) { self.parser.next_token(); Ok(Statement::ShowCharset(self.parse_show_kind()?)) @@ -430,6 +432,28 @@ impl<'a> ParserContext<'a> { _ => self.unsupported(self.peek_token_as_string()), } } + + fn parse_show_views(&mut self) -> Result { + let database = match self.parser.peek_token().token { + Token::EOF | Token::SemiColon => { + return Ok(Statement::ShowViews(ShowViews { + kind: ShowKind::All, + database: None, + })); + } + + // SHOW VIEWS [in | FROM] [DATABASE] + Token::Word(w) => match w.keyword { + Keyword::IN | Keyword::FROM => self.parse_db_name()?, + _ => None, + }, + _ => None, + }; + + let kind = self.parse_show_kind()?; + + Ok(Statement::ShowViews(ShowViews { kind, database })) + } } #[cfg(test)] @@ -942,4 +966,38 @@ mod tests { ); assert_eq!(sql, stmts[0].to_string()); } + + #[test] + pub fn test_show_views() { + let sql = "SHOW VIEWS"; + let result = + ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default()); + let stmts = result.unwrap(); + assert_eq!(1, stmts.len()); + assert_eq!( + stmts[0], + Statement::ShowViews(ShowViews { + kind: ShowKind::All, + database: None, + }) + ); + assert_eq!(sql, stmts[0].to_string()); + } + + #[test] + pub fn test_show_views_in_db() { + let sql = "SHOW VIEWS IN d1"; + let result = + ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default()); + let stmts = result.unwrap(); + assert_eq!(1, stmts.len()); + assert_eq!( + stmts[0], + Statement::ShowViews(ShowViews { + kind: ShowKind::All, + database: Some("d1".to_string()), + }) + ); + assert_eq!(sql, stmts[0].to_string()); + } } diff --git a/src/sql/src/statements/show.rs b/src/sql/src/statements/show.rs index ac41085cb164..abb3488dbcc4 100644 --- a/src/sql/src/statements/show.rs +++ b/src/sql/src/statements/show.rs @@ -200,6 +200,25 @@ impl Display for ShowCreateView { } } +/// SQL structure for `SHOW VIEWS`. +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +pub struct ShowViews { + pub kind: ShowKind, + pub database: Option, +} + +impl Display for ShowViews { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SHOW VIEWS")?; + if let Some(database) = &self.database { + write!(f, " IN {database}")?; + } + format_kind!(self, f); + + Ok(()) + } +} + /// SQL structure for `SHOW VARIABLES xxx`. #[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] pub struct ShowVariables { @@ -473,6 +492,49 @@ SHOW FULL TABLES"#, } } + #[test] + fn test_display_show_views() { + let sql = r"show views in d1;"; + let stmts: Vec = + ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default()) + .unwrap(); + assert_eq!(1, stmts.len()); + assert_matches!(&stmts[0], Statement::ShowViews { .. }); + match &stmts[0] { + Statement::ShowViews(show) => { + let new_sql = format!("\n{}", show); + assert_eq!( + r#" +SHOW VIEWS IN d1"#, + &new_sql + ); + } + _ => { + unreachable!(); + } + } + + let sql = r"show views;"; + let stmts: Vec = + ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default()) + .unwrap(); + assert_eq!(1, stmts.len()); + assert_matches!(&stmts[0], Statement::ShowViews { .. }); + match &stmts[0] { + Statement::ShowViews(show) => { + let new_sql = format!("\n{}", show); + assert_eq!( + r#" +SHOW VIEWS"#, + &new_sql + ); + } + _ => { + unreachable!(); + } + } + } + #[test] fn test_display_show_databases() { let sql = r"show databases;"; diff --git a/src/sql/src/statements/statement.rs b/src/sql/src/statements/statement.rs index ab7f4ffba55e..5dbe243a54f8 100644 --- a/src/sql/src/statements/statement.rs +++ b/src/sql/src/statements/statement.rs @@ -32,7 +32,7 @@ use crate::statements::query::Query; use crate::statements::set_variables::SetVariables; use crate::statements::show::{ ShowColumns, ShowCreateFlow, ShowCreateTable, ShowCreateView, ShowDatabases, ShowIndex, - ShowKind, ShowStatus, ShowTableStatus, ShowTables, ShowVariables, + ShowKind, ShowStatus, ShowTableStatus, ShowTables, ShowVariables, ShowViews, }; use crate::statements::tql::Tql; use crate::statements::truncate::TruncateTable; @@ -91,6 +91,8 @@ pub enum Statement { ShowCreateView(ShowCreateView), // SHOW STATUS ShowStatus(ShowStatus), + // SHOW VIEWS + ShowViews(ShowViews), // DESCRIBE TABLE DescribeTable(DescribeTable), // EXPLAIN QUERY @@ -132,6 +134,7 @@ impl Display for Statement { Statement::ShowCreateTable(s) => s.fmt(f), Statement::ShowCreateFlow(s) => s.fmt(f), Statement::ShowCreateView(s) => s.fmt(f), + Statement::ShowViews(s) => s.fmt(f), Statement::ShowStatus(s) => s.fmt(f), Statement::DescribeTable(s) => s.fmt(f), Statement::Explain(s) => s.fmt(f), diff --git a/tests/cases/standalone/common/view/view.result b/tests/cases/standalone/common/view/view.result index c9346925e12a..7834a7e7c2fb 100644 --- a/tests/cases/standalone/common/view/view.result +++ b/tests/cases/standalone/common/view/view.result @@ -52,6 +52,14 @@ CREATE VIEW v1 AS SELECT * FROM dontexist; Error: 3000(PlanQuery), Failed to plan SQL: Error during planning: Table not found: greptime.public.dontexist +SHOW VIEWS; + ++-------+ +| Views | ++-------+ +| v1 | ++-------+ + DROP VIEW v1; Affected Rows: 0 @@ -81,3 +89,8 @@ SHOW TABLES; | numbers | +---------+ +SHOW VIEWS; + +++ +++ + diff --git a/tests/cases/standalone/common/view/view.sql b/tests/cases/standalone/common/view/view.sql index 84cf1cdd9da7..9c5efc01b5b9 100644 --- a/tests/cases/standalone/common/view/view.sql +++ b/tests/cases/standalone/common/view/view.sql @@ -30,6 +30,8 @@ INSERT INTO v1 VALUES (1); CREATE VIEW v1 AS SELECT * FROM dontexist; +SHOW VIEWS; + DROP VIEW v1; SELECT * FROM v1; @@ -42,3 +44,5 @@ DROP VIEW IF EXISTS v2; DROP TABLE t1; SHOW TABLES; + +SHOW VIEWS;