From 7e16006ae98882902aa05f505ba5bbea32425547 Mon Sep 17 00:00:00 2001 From: August Date: Thu, 15 Dec 2022 19:37:01 +0800 Subject: [PATCH 1/3] feat: support show create on mview and mview --- src/frontend/src/handler/mod.rs | 3 ++ src/frontend/src/handler/show.rs | 63 +++++++++++++++++++++++++++++++- src/sqlparser/src/ast/mod.rs | 37 ++++++++++++++++++- src/sqlparser/src/parser.rs | 42 ++++++++++++++++++++- 4 files changed, 141 insertions(+), 4 deletions(-) diff --git a/src/frontend/src/handler/mod.rs b/src/frontend/src/handler/mod.rs index 40575e36ed043..2e4c038ab9316 100644 --- a/src/frontend/src/handler/mod.rs +++ b/src/frontend/src/handler/mod.rs @@ -173,6 +173,9 @@ pub async fn handle( } Statement::Describe { name } => describe::handle_describe(handler_args, name), Statement::ShowObjects(show_object) => show::handle_show_object(handler_args, show_object), + Statement::ShowCreateObject { create_type, name } => { + show::handle_show_create_object(handler_args, create_type, name) + } Statement::Drop(DropStatement { object_type, object_name, diff --git a/src/frontend/src/handler/show.rs b/src/frontend/src/handler/show.rs index 38f38332e2e57..e9b71cc92db5c 100644 --- a/src/frontend/src/handler/show.rs +++ b/src/frontend/src/handler/show.rs @@ -17,9 +17,9 @@ use pgwire::pg_field_descriptor::PgFieldDescriptor; use pgwire::pg_response::{PgResponse, StatementType}; use pgwire::types::Row; use risingwave_common::catalog::{ColumnDesc, DEFAULT_SCHEMA_NAME}; -use risingwave_common::error::Result; +use risingwave_common::error::{ErrorCode, Result, RwError}; use risingwave_common::types::DataType; -use risingwave_sqlparser::ast::{Ident, ObjectName, ShowObject}; +use risingwave_sqlparser::ast::{Ident, ObjectName, ShowCreateType, ShowObject}; use super::RwPgResponse; use crate::binder::{Binder, Relation}; @@ -131,6 +131,65 @@ pub fn handle_show_object(handler_args: HandlerArgs, command: ShowObject) -> Res )) } +pub fn handle_show_create_object( + handle_args: HandlerArgs, + show_create_type: ShowCreateType, + name: ObjectName, +) -> Result { + let session = handle_args.session; + let catalog_reader = session.env().catalog_reader().read_guard(); + let (schema_name, object_name) = + Binder::resolve_schema_qualified_name(session.database(), name.clone())?; + let schema_name = schema_name.unwrap_or(DEFAULT_SCHEMA_NAME.to_string()); + let schema = catalog_reader.get_schema_by_name(session.database(), &schema_name)?; + let sql = match show_create_type { + ShowCreateType::MaterializedView => { + let table = schema.get_table_by_name(&object_name).ok_or_else(|| { + RwError::from(CatalogError::NotFound( + "materialized view", + name.to_string(), + )) + })?; + if !table.is_mview() { + return Err(CatalogError::NotFound("materialized view", name.to_string()).into()); + } + table.definition.clone() + } + ShowCreateType::View => { + let view = schema + .get_view_by_name(&object_name) + .ok_or_else(|| RwError::from(CatalogError::NotFound("view", name.to_string())))?; + view.sql.clone() + } + _ => { + return Err(ErrorCode::NotImplemented( + format!("show create on: {}", show_create_type), + None.into(), + ) + .into()); + } + }; + let name = format!("{}.{}.{}", session.database(), schema_name, object_name); + + return Ok(PgResponse::new_for_stream( + StatementType::SHOW_COMMAND, + Some(1), + vec![Row::new(vec![Some(name.into()), Some(sql.into())])].into(), + vec![ + PgFieldDescriptor::new( + "Name".to_owned(), + DataType::VARCHAR.to_oid(), + DataType::VARCHAR.type_len(), + ), + PgFieldDescriptor::new( + "Create Sql".to_owned(), + DataType::VARCHAR.to_oid(), + DataType::VARCHAR.type_len(), + ), + ], + )); +} + #[cfg(test)] mod tests { use std::collections::HashMap; diff --git a/src/sqlparser/src/ast/mod.rs b/src/sqlparser/src/ast/mod.rs index 6e349f5a10330..34161b558c418 100644 --- a/src/sqlparser/src/ast/mod.rs +++ b/src/sqlparser/src/ast/mod.rs @@ -739,6 +739,30 @@ impl fmt::Display for ShowObject { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum ShowCreateType { + Table, + MaterializedView, + View, + Index, + Source, + Sink, +} + +impl fmt::Display for ShowCreateType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ShowCreateType::Table => f.write_str("TABLE"), + ShowCreateType::MaterializedView => f.write_str("MATERIALIZED VIEW"), + ShowCreateType::View => f.write_str("VIEW"), + ShowCreateType::Index => f.write_str("INDEX"), + ShowCreateType::Source => f.write_str("SOURCE"), + ShowCreateType::Sink => f.write_str("SINK"), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum CommentObject { @@ -920,8 +944,15 @@ pub enum Statement { /// Table or Source name name: ObjectName, }, - /// SHOW COMMAND + /// SHOW OBJECT COMMAND ShowObjects(ShowObject), + /// SHOW CREATE COMMAND + ShowCreateObject { + /// Show create object type + create_type: ShowCreateType, + /// Show create object name + name: ObjectName, + }, /// DROP Drop(DropStatement), /// SET @@ -1061,6 +1092,10 @@ impl fmt::Display for Statement { write!(f, "SHOW {}", show_object)?; Ok(()) } + Statement::ShowCreateObject{ create_type: show_type, name } => { + write!(f, "SHOW CREATE {} {}", show_type, name)?; + Ok(()) + } Statement::Insert { table_name, columns, diff --git a/src/sqlparser/src/parser.rs b/src/sqlparser/src/parser.rs index 9e290567e01e6..c8194f9782112 100644 --- a/src/sqlparser/src/parser.rs +++ b/src/sqlparser/src/parser.rs @@ -181,7 +181,13 @@ impl Parser { Keyword::ALTER => Ok(self.parse_alter()?), Keyword::COPY => Ok(self.parse_copy()?), Keyword::SET => Ok(self.parse_set()?), - Keyword::SHOW => Ok(self.parse_show()?), + Keyword::SHOW => { + if self.parse_keyword(Keyword::CREATE) { + Ok(self.parse_show_create()?) + } else { + Ok(self.parse_show()?) + } + } Keyword::DESCRIBE => Ok(Statement::Describe { name: self.parse_object_name()?, }), @@ -2925,6 +2931,40 @@ impl Parser { } } + /// Parse object type and name after `show create`. + pub fn parse_show_create(&mut self) -> Result { + if let Token::Word(w) = self.next_token() { + let show_type = match w.keyword { + Keyword::TABLE => ShowCreateType::Table, + Keyword::MATERIALIZED => { + if self.parse_keyword(Keyword::VIEW) { + ShowCreateType::MaterializedView + } else { + return self.expected("VIEW after MATERIALIZED", self.peek_token()); + } + } + Keyword::VIEW => ShowCreateType::View, + Keyword::INDEX => ShowCreateType::Index, + Keyword::SOURCE => ShowCreateType::Source, + Keyword::SINK => ShowCreateType::Sink, + _ => { + return self.expected( + "TABLE, MATERIALIZED VIEW, VIEW, INDEX, SOURCE or SINK", + self.peek_token(), + ) + } + }; + return Ok(Statement::ShowCreateObject { + create_type: show_type, + name: self.parse_object_name()?, + }); + } + self.expected( + "TABLE, MATERIALIZED VIEW, VIEW, INDEX, SOURCE or SINK", + self.peek_token(), + ) + } + pub fn parse_table_and_joins(&mut self) -> Result { let relation = self.parse_table_factor()?; From 0d5e5c47b596eb14e407267991b91cfa9414cd2d Mon Sep 17 00:00:00 2001 From: August Date: Thu, 15 Dec 2022 21:23:15 +0800 Subject: [PATCH 2/3] fmt and add some test --- e2e_test/ddl/show.slt | 16 ++++++++++++++++ src/frontend/src/handler/show.rs | 17 ++++++++++++++--- src/sqlparser/tests/testdata/show.yaml | 8 ++++++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/e2e_test/ddl/show.slt b/e2e_test/ddl/show.slt index 5db68aa650732..0419746d60cf9 100644 --- a/e2e_test/ddl/show.slt +++ b/e2e_test/ddl/show.slt @@ -72,9 +72,25 @@ query T show sources; ---- +query TT +show create materialized view mv3; +---- +public.mv3 CREATE MATERIALIZED VIEW mv3 AS SELECT sum(v1) AS sum_v1 FROM t3 + +statement ok +create view v1 as select * from t3; + +query TT +show create view v1; +---- +public.v1 CREATE VIEW v1 AS SELECT * FROM t3 + statement ok drop materialized view mv3; +statement ok +drop view v1; + statement ok drop table t3; diff --git a/src/frontend/src/handler/show.rs b/src/frontend/src/handler/show.rs index e9b71cc92db5c..d27b3d1cdd070 100644 --- a/src/frontend/src/handler/show.rs +++ b/src/frontend/src/handler/show.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use anyhow::anyhow; use itertools::Itertools; use pgwire::pg_field_descriptor::PgFieldDescriptor; use pgwire::pg_response::{PgResponse, StatementType}; @@ -20,6 +21,7 @@ use risingwave_common::catalog::{ColumnDesc, DEFAULT_SCHEMA_NAME}; use risingwave_common::error::{ErrorCode, Result, RwError}; use risingwave_common::types::DataType; use risingwave_sqlparser::ast::{Ident, ObjectName, ShowCreateType, ShowObject}; +use risingwave_sqlparser::parser::Parser; use super::RwPgResponse; use crate::binder::{Binder, Relation}; @@ -153,13 +155,22 @@ pub fn handle_show_create_object( if !table.is_mview() { return Err(CatalogError::NotFound("materialized view", name.to_string()).into()); } - table.definition.clone() + // We only stored the definition of materialized view, format and return directly. + format!( + "CREATE MATERIALIZED VIEW {} AS {}", + table.name, table.definition + ) } ShowCreateType::View => { let view = schema .get_view_by_name(&object_name) .ok_or_else(|| RwError::from(CatalogError::NotFound("view", name.to_string())))?; - view.sql.clone() + + // We only stored original sql in catalog, uses parser to parse and format. + let stmt = Parser::parse_sql(&view.sql) + .map_err(|err| anyhow!("Failed to parse view create sql: {}, {}", view.sql, err))?; + assert!(stmt.len() == 1); + stmt[0].to_string() } _ => { return Err(ErrorCode::NotImplemented( @@ -169,7 +180,7 @@ pub fn handle_show_create_object( .into()); } }; - let name = format!("{}.{}.{}", session.database(), schema_name, object_name); + let name = format!("{}.{}", schema_name, object_name); return Ok(PgResponse::new_for_stream( StatementType::SHOW_COMMAND, diff --git a/src/sqlparser/tests/testdata/show.yaml b/src/sqlparser/tests/testdata/show.yaml index 5da8f5cf8198d..816124e5ea8c7 100644 --- a/src/sqlparser/tests/testdata/show.yaml +++ b/src/sqlparser/tests/testdata/show.yaml @@ -48,4 +48,12 @@ formatted_ast: | ShowObjects(Columns { table: ObjectName([Ident { value: "schema", quote_style: None }, Ident { value: "t", quote_style: None }]) }) +- input: SHOW CREATE MATERIALIZED VIEW schema.mv + formatted_sql: SHOW CREATE MATERIALIZED VIEW schema.mv + formatted_ast: | + ShowCreateObject { create_type: MaterializedView, name: ObjectName([Ident { value: "schema", quote_style: None }, Ident { value: "mv", quote_style: None }]) } +- input: SHOW CREATE VIEW schema.v + formatted_sql: SHOW CREATE VIEW schema.v + formatted_ast: | + ShowCreateObject { create_type: View, name: ObjectName([Ident { value: "schema", quote_style: None }, Ident { value: "v", quote_style: None }]) } From 45a6c2832abaff79210ea6600833f6fbe1c6d38e Mon Sep 17 00:00:00 2001 From: August Date: Thu, 15 Dec 2022 21:32:40 +0800 Subject: [PATCH 3/3] fmt --- src/frontend/src/handler/show.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/src/handler/show.rs b/src/frontend/src/handler/show.rs index d27b3d1cdd070..ada7558a54ede 100644 --- a/src/frontend/src/handler/show.rs +++ b/src/frontend/src/handler/show.rs @@ -182,7 +182,7 @@ pub fn handle_show_create_object( }; let name = format!("{}.{}", schema_name, object_name); - return Ok(PgResponse::new_for_stream( + Ok(PgResponse::new_for_stream( StatementType::SHOW_COMMAND, Some(1), vec![Row::new(vec![Some(name.into()), Some(sql.into())])].into(), @@ -198,7 +198,7 @@ pub fn handle_show_create_object( DataType::VARCHAR.type_len(), ), ], - )); + )) } #[cfg(test)]