From cff8fe4e0e87237b4e99832a87e8a5a87cb1265b Mon Sep 17 00:00:00 2001 From: Sheng hui <67598635+ck-567@users.noreply.github.com> Date: Tue, 8 Nov 2022 16:35:56 +0800 Subject: [PATCH] feat: Allow sql parser to parse show-create-table statement (#347) * feat: Add ShowCreateTable to Statement * feat: Add ShowCreateTable to Statement * feat: Add ShowCreateTable to Statement * feat: Add ShowCreateTable to Statement * feat: Add ShowCreateTable to Statement * feat: Add ShowCreateTable to Statement * feat: Add ShowCreateTable to Statement * feat: Add ShowCreateTable to Statement * feat: Add ShowCreateTable to Statement --- src/datanode/src/instance/sql.rs | 3 +++ src/query/src/datafusion/planner.rs | 1 + src/sql/src/error.rs | 7 +++++- src/sql/src/parser.rs | 33 +++++++++++++++++++++++++++-- src/sql/src/statements/show.rs | 29 +++++++++++++++++++++++++ src/sql/src/statements/statement.rs | 7 +++++- 6 files changed, 76 insertions(+), 4 deletions(-) diff --git a/src/datanode/src/instance/sql.rs b/src/datanode/src/instance/sql.rs index 004d247bdd..28698ab00a 100644 --- a/src/datanode/src/instance/sql.rs +++ b/src/datanode/src/instance/sql.rs @@ -77,6 +77,9 @@ impl Instance { Statement::ShowTables(stmt) => { self.sql_handler.execute(SqlRequest::ShowTables(stmt)).await } + Statement::ShowCreateTable(_stmt) => { + unimplemented!("SHOW CREATE TABLE is unimplemented yet"); + } } } } diff --git a/src/query/src/datafusion/planner.rs b/src/query/src/datafusion/planner.rs index b0b5fffdd2..4e0a58bdc2 100644 --- a/src/query/src/datafusion/planner.rs +++ b/src/query/src/datafusion/planner.rs @@ -48,6 +48,7 @@ where Statement::Query(qb) => self.query_to_plan(qb), Statement::ShowTables(_) | Statement::ShowDatabases(_) + | Statement::ShowCreateTable(_) | Statement::Create(_) | Statement::Alter(_) | Statement::Insert(_) => unreachable!(), diff --git a/src/sql/src/error.rs b/src/sql/src/error.rs index 632a50ec6b..7724a1ee24 100644 --- a/src/sql/src/error.rs +++ b/src/sql/src/error.rs @@ -83,6 +83,9 @@ pub enum Error { #[snafu(display("Invalid database name: {}", name))] InvalidDatabaseName { name: String, backtrace: Backtrace }, + #[snafu(display("Invalid table name: {}", name))] + InvalidTableName { name: String, backtrace: Backtrace }, + #[snafu(display("Invalid default constraint, column: {}, source: {}", column, source))] InvalidDefault { column: String, @@ -106,7 +109,9 @@ impl ErrorExt for Error { | SqlTypeNotSupported { .. } | InvalidDefault { .. } => StatusCode::InvalidSyntax, - InvalidDatabaseName { .. } | ColumnTypeMismatch { .. } => StatusCode::InvalidArguments, + InvalidDatabaseName { .. } | ColumnTypeMismatch { .. } | InvalidTableName { .. } => { + StatusCode::InvalidArguments + } } } diff --git a/src/sql/src/parser.rs b/src/sql/src/parser.rs index 0557439bec..1bb7f3999a 100644 --- a/src/sql/src/parser.rs +++ b/src/sql/src/parser.rs @@ -5,8 +5,10 @@ use sqlparser::parser::Parser; use sqlparser::parser::ParserError; use sqlparser::tokenizer::{Token, Tokenizer}; -use crate::error::{self, InvalidDatabaseNameSnafu, Result, SyntaxSnafu, TokenizerSnafu}; -use crate::statements::show::{ShowDatabases, ShowKind, ShowTables}; +use crate::error::{ + self, InvalidDatabaseNameSnafu, InvalidTableNameSnafu, Result, SyntaxSnafu, TokenizerSnafu, +}; +use crate::statements::show::{ShowCreateTable, ShowDatabases, ShowKind, ShowTables}; use crate::statements::statement::Statement; /// GrepTime SQL parser context, a simple wrapper for Datafusion SQL parser. @@ -102,11 +104,38 @@ impl<'a> ParserContext<'a> { } else if self.matches_keyword(Keyword::TABLES) { self.parser.next_token(); self.parse_show_tables() + } else if self.consume_token("CREATE") { + if self.consume_token("TABLE") { + self.parse_show_create_table() + } else { + self.unsupported(self.peek_token_as_string()) + } } else { self.unsupported(self.peek_token_as_string()) } } + /// Parse SHOW CREATE TABLE statement + fn parse_show_create_table(&mut self) -> Result { + let table_name = + self.parser + .parse_object_name() + .with_context(|_| error::UnexpectedSnafu { + sql: self.sql, + expected: "a table name", + actual: self.peek_token_as_string(), + })?; + ensure!( + !table_name.0.is_empty(), + InvalidTableNameSnafu { + name: table_name.to_string(), + } + ); + Ok(Statement::ShowCreateTable(ShowCreateTable { + table_name: table_name.to_string(), + })) + } + fn parse_show_tables(&mut self) -> Result { let database = match self.parser.peek_token() { Token::EOF | Token::SemiColon => { diff --git a/src/sql/src/statements/show.rs b/src/sql/src/statements/show.rs index 39f7e66dd4..ae0f348a72 100644 --- a/src/sql/src/statements/show.rs +++ b/src/sql/src/statements/show.rs @@ -40,6 +40,12 @@ pub struct ShowTables { pub database: Option, } +/// SQL structure for `SHOW CREATE TABLE`. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ShowCreateTable { + pub table_name: String, +} + #[cfg(test)] mod tests { use std::assert_matches::assert_matches; @@ -94,4 +100,27 @@ mod tests { } } } + + #[test] + pub fn test_show_create_table() { + let sql = "SHOW CREATE TABLE test"; + let stmts: Vec = + ParserContext::create_with_dialect(sql, &GenericDialect {}).unwrap(); + assert_eq!(1, stmts.len()); + assert_matches!(&stmts[0], Statement::ShowCreateTable { .. }); + match &stmts[0] { + Statement::ShowCreateTable(show) => { + let table_name = show.table_name.as_str(); + assert_eq!(table_name, "test"); + } + _ => { + unreachable!(); + } + } + } + #[test] + pub fn test_show_create_missing_table_name() { + let sql = "SHOW CREATE TABLE"; + ParserContext::create_with_dialect(sql, &GenericDialect {}).unwrap_err(); + } } diff --git a/src/sql/src/statements/statement.rs b/src/sql/src/statements/statement.rs index a2b5b92855..3bb3473cb0 100644 --- a/src/sql/src/statements/statement.rs +++ b/src/sql/src/statements/statement.rs @@ -5,7 +5,7 @@ use crate::statements::alter::AlterTable; use crate::statements::create_table::CreateTable; use crate::statements::insert::Insert; use crate::statements::query::Query; -use crate::statements::show::{ShowDatabases, ShowTables}; +use crate::statements::show::{ShowCreateTable, ShowDatabases, ShowTables}; /// Tokens parsed by `DFParser` are converted into these values. #[derive(Debug, Clone, PartialEq, Eq)] @@ -22,6 +22,8 @@ pub enum Statement { ShowDatabases(ShowDatabases), // SHOW TABLES ShowTables(ShowTables), + // SHOW CREATE TABLE + ShowCreateTable(ShowCreateTable), } /// Converts Statement to sqlparser statement @@ -36,6 +38,9 @@ impl TryFrom for SpStatement { Statement::ShowTables(_) => Err(ParserError::ParserError( "sqlparser does not support SHOW TABLES query.".to_string(), )), + Statement::ShowCreateTable(_) => Err(ParserError::ParserError( + "sqlparser does not support SHOW CREATE TABLE query.".to_string(), + )), Statement::Query(s) => Ok(SpStatement::Query(Box::new(s.inner))), Statement::Insert(i) => Ok(i.inner), Statement::Create(_) | Statement::Alter(_) => unimplemented!(),