refactor: Separating statement parse func (#1975)

* refactor:Separating statement parse func

* refactor:refactor describe,explain and drop

* Update src/sql/src/parser.rs

---------

Co-authored-by: LFC <bayinamine@gmail.com>
This commit is contained in:
Ben Baoyi
2023-07-19 05:22:36 +00:00
committed by GitHub
co-authored by LFC
parent b227a7637c
commit 3241de0b85
6 changed files with 581 additions and 457 deletions
+2 -457
View File
@@ -12,19 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use snafu::{ensure, ResultExt};
use snafu::ResultExt;
use sqlparser::dialect::Dialect;
use sqlparser::keywords::Keyword;
use sqlparser::parser::{Parser, ParserError};
use sqlparser::tokenizer::{Token, TokenWithLocation};
use crate::ast::{Expr, ObjectName};
use crate::error::{self, InvalidDatabaseNameSnafu, InvalidTableNameSnafu, Result, SyntaxSnafu};
use crate::error::{self, Result, SyntaxSnafu};
use crate::parsers::tql_parser;
use crate::statements::describe::DescribeTable;
use crate::statements::drop::DropTable;
use crate::statements::explain::Explain;
use crate::statements::show::{ShowCreateTable, ShowDatabases, ShowKind, ShowTables};
use crate::statements::statement::Statement;
/// GrepTime SQL parser context, a simple wrapper for Datafusion SQL parser.
@@ -153,176 +149,6 @@ impl<'a> ParserContext<'a> {
.fail()
}
/// Parses SHOW statements
/// todo(hl) support `show settings`/`show create`/`show users` etc.
fn parse_show(&mut self) -> Result<Statement> {
if self.consume_token("DATABASES") || self.consume_token("SCHEMAS") {
self.parse_show_databases()
} else if self.matches_keyword(Keyword::TABLES) {
let _ = 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<Statement> {
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 }))
}
fn parse_show_tables(&mut self) -> Result<Statement> {
let database = match self.parser.peek_token().token {
Token::EOF | Token::SemiColon => {
return Ok(Statement::ShowTables(ShowTables {
kind: ShowKind::All,
database: None,
}));
}
// SHOW TABLES [in | FROM] [DATABASE]
Token::Word(w) => match w.keyword {
Keyword::IN | Keyword::FROM => {
let _ = self.parser.next_token();
let db_name = self.parser.parse_object_name().with_context(|_| {
error::UnexpectedSnafu {
sql: self.sql,
expected: "a database name",
actual: self.peek_token_as_string(),
}
})?;
ensure!(
db_name.0.len() == 1,
InvalidDatabaseNameSnafu {
name: db_name.to_string(),
}
);
Some(db_name.to_string())
}
_ => None,
},
_ => None,
};
let kind = match self.parser.peek_token().token {
Token::EOF | Token::SemiColon => ShowKind::All,
// SHOW TABLES [WHERE | LIKE] [EXPR]
Token::Word(w) => match w.keyword {
Keyword::LIKE => {
let _ = self.parser.next_token();
ShowKind::Like(self.parser.parse_identifier().with_context(|_| {
error::UnexpectedSnafu {
sql: self.sql,
expected: "LIKE",
actual: self.peek_token_as_string(),
}
})?)
}
Keyword::WHERE => {
let _ = self.parser.next_token();
ShowKind::Where(self.parser.parse_expr().with_context(|_| {
error::UnexpectedSnafu {
sql: self.sql,
expected: "some valid expression",
actual: self.peek_token_as_string(),
}
})?)
}
_ => return self.unsupported(self.peek_token_as_string()),
},
_ => return self.unsupported(self.peek_token_as_string()),
};
Ok(Statement::ShowTables(ShowTables { kind, database }))
}
/// Parses DESCRIBE statements
fn parse_describe(&mut self) -> Result<Statement> {
if self.matches_keyword(Keyword::TABLE) {
let _ = self.parser.next_token();
}
self.parse_describe_table()
}
fn parse_describe_table(&mut self) -> Result<Statement> {
let table_idents =
self.parser
.parse_object_name()
.with_context(|_| error::UnexpectedSnafu {
sql: self.sql,
expected: "a table name",
actual: self.peek_token_as_string(),
})?;
ensure!(
!table_idents.0.is_empty(),
InvalidTableNameSnafu {
name: table_idents.to_string(),
}
);
Ok(Statement::DescribeTable(DescribeTable::new(table_idents)))
}
fn parse_explain(&mut self) -> Result<Statement> {
let explain_statement =
self.parser
.parse_explain(false)
.with_context(|_| error::UnexpectedSnafu {
sql: self.sql,
expected: "a query statement",
actual: self.peek_token_as_string(),
})?;
Ok(Statement::Explain(Explain::try_from(explain_statement)?))
}
fn parse_drop(&mut self) -> Result<Statement> {
let _ = self.parser.next_token();
if !self.matches_keyword(Keyword::TABLE) {
return self.unsupported(self.peek_token_as_string());
}
let _ = self.parser.next_token();
let table_ident =
self.parser
.parse_object_name()
.with_context(|_| error::UnexpectedSnafu {
sql: self.sql,
expected: "a table name",
actual: self.peek_token_as_string(),
})?;
ensure!(
!table_ident.0.is_empty(),
InvalidTableNameSnafu {
name: table_ident.to_string()
}
);
Ok(Statement::DropTable(DropTable::new(table_ident)))
}
// Report unexpected token
pub(crate) fn expected<T>(&self, expected: &str, found: TokenWithLocation) -> Result<T> {
Err(ParserError::ParserError(format!(
@@ -351,276 +177,18 @@ impl<'a> ParserContext<'a> {
pub(crate) fn peek_token_as_string(&self) -> String {
self.parser.peek_token().to_string()
}
/// Parses `SHOW DATABASES` statement.
pub fn parse_show_databases(&mut self) -> Result<Statement> {
let tok = self.parser.next_token().token;
match &tok {
Token::EOF | Token::SemiColon => {
Ok(Statement::ShowDatabases(ShowDatabases::new(ShowKind::All)))
}
Token::Word(w) => match w.keyword {
Keyword::LIKE => Ok(Statement::ShowDatabases(ShowDatabases::new(
ShowKind::Like(self.parser.parse_identifier().with_context(|_| {
error::UnexpectedSnafu {
sql: self.sql,
expected: "LIKE",
actual: tok.to_string(),
}
})?),
))),
Keyword::WHERE => Ok(Statement::ShowDatabases(ShowDatabases::new(
ShowKind::Where(self.parser.parse_expr().with_context(|_| {
error::UnexpectedSnafu {
sql: self.sql,
expected: "some valid expression",
actual: self.peek_token_as_string(),
}
})?),
))),
_ => self.unsupported(self.peek_token_as_string()),
},
_ => self.unsupported(self.peek_token_as_string()),
}
}
}
#[cfg(test)]
mod tests {
use std::assert_matches::assert_matches;
use datatypes::prelude::ConcreteDataType;
use sqlparser::ast::{
Ident, ObjectName, Query as SpQuery, Statement as SpStatement, WildcardAdditionalOptions,
};
use super::*;
use crate::dialect::GreptimeDbDialect;
use crate::statements::create::CreateTable;
use crate::statements::sql_data_type_to_concrete_data_type;
#[test]
pub fn test_show_database_all() {
let sql = "SHOW DATABASES";
let result = ParserContext::create_with_dialect(sql, &GreptimeDbDialect {});
let stmts = result.unwrap();
assert_eq!(1, stmts.len());
assert_matches!(
&stmts[0],
Statement::ShowDatabases(ShowDatabases {
kind: ShowKind::All
})
);
}
#[test]
pub fn test_show_database_like() {
let sql = "SHOW DATABASES LIKE test_database";
let result = ParserContext::create_with_dialect(sql, &GreptimeDbDialect {});
let stmts = result.unwrap();
assert_eq!(1, stmts.len());
assert_matches!(
&stmts[0],
Statement::ShowDatabases(ShowDatabases {
kind: ShowKind::Like(sqlparser::ast::Ident {
value: _,
quote_style: None,
})
})
);
}
#[test]
pub fn test_show_database_where() {
let sql = "SHOW DATABASES WHERE Database LIKE '%whatever1%' OR Database LIKE '%whatever2%'";
let result = ParserContext::create_with_dialect(sql, &GreptimeDbDialect {});
let stmts = result.unwrap();
assert_eq!(1, stmts.len());
assert_matches!(
&stmts[0],
Statement::ShowDatabases(ShowDatabases {
kind: ShowKind::Where(sqlparser::ast::Expr::BinaryOp {
left: _,
right: _,
op: sqlparser::ast::BinaryOperator::Or,
})
})
);
}
#[test]
pub fn test_show_tables_all() {
let sql = "SHOW TABLES";
let result = ParserContext::create_with_dialect(sql, &GreptimeDbDialect {});
let stmts = result.unwrap();
assert_eq!(1, stmts.len());
assert_matches!(
&stmts[0],
Statement::ShowTables(ShowTables {
kind: ShowKind::All,
database: None,
})
);
}
#[test]
pub fn test_show_tables_like() {
let sql = "SHOW TABLES LIKE test_table";
let result = ParserContext::create_with_dialect(sql, &GreptimeDbDialect {});
let stmts = result.unwrap();
assert_eq!(1, stmts.len());
assert_matches!(
&stmts[0],
Statement::ShowTables(ShowTables {
kind: ShowKind::Like(sqlparser::ast::Ident {
value: _,
quote_style: None,
}),
database: None,
})
);
let sql = "SHOW TABLES in test_db LIKE test_table";
let result = ParserContext::create_with_dialect(sql, &GreptimeDbDialect {});
let stmts = result.unwrap();
assert_eq!(1, stmts.len());
assert_matches!(
&stmts[0],
Statement::ShowTables(ShowTables {
kind: ShowKind::Like(sqlparser::ast::Ident {
value: _,
quote_style: None,
}),
database: Some(_),
})
);
}
#[test]
pub fn test_show_tables_where() {
let sql = "SHOW TABLES where name like test_table";
let result = ParserContext::create_with_dialect(sql, &GreptimeDbDialect {});
let stmts = result.unwrap();
assert_eq!(1, stmts.len());
assert_matches!(
&stmts[0],
Statement::ShowTables(ShowTables {
kind: ShowKind::Where(sqlparser::ast::Expr::Like { .. }),
database: None,
})
);
let sql = "SHOW TABLES in test_db where name LIKE test_table";
let result = ParserContext::create_with_dialect(sql, &GreptimeDbDialect {});
let stmts = result.unwrap();
assert_eq!(1, stmts.len());
assert_matches!(
&stmts[0],
Statement::ShowTables(ShowTables {
kind: ShowKind::Where(sqlparser::ast::Expr::Like { .. }),
database: Some(_),
})
);
}
#[test]
pub fn test_explain() {
let sql = "EXPLAIN select * from foo";
let result = ParserContext::create_with_dialect(sql, &GreptimeDbDialect {});
let stmts = result.unwrap();
assert_eq!(1, stmts.len());
let select = sqlparser::ast::Select {
distinct: None,
top: None,
projection: vec![sqlparser::ast::SelectItem::Wildcard(
WildcardAdditionalOptions::default(),
)],
into: None,
from: vec![sqlparser::ast::TableWithJoins {
relation: sqlparser::ast::TableFactor::Table {
name: sqlparser::ast::ObjectName(vec![sqlparser::ast::Ident::new("foo")]),
alias: None,
args: None,
with_hints: vec![],
},
joins: vec![],
}],
lateral_views: vec![],
selection: None,
group_by: vec![],
cluster_by: vec![],
distribute_by: vec![],
sort_by: vec![],
having: None,
qualify: None,
named_window: vec![],
};
let sp_statement = SpStatement::Query(Box::new(SpQuery {
with: None,
body: Box::new(sqlparser::ast::SetExpr::Select(Box::new(select))),
order_by: vec![],
limit: None,
offset: None,
fetch: None,
locks: vec![],
}));
let explain = Explain::try_from(SpStatement::Explain {
describe_alias: false,
analyze: false,
verbose: false,
statement: Box::new(sp_statement),
format: None,
})
.unwrap();
assert_eq!(stmts[0], Statement::Explain(explain))
}
#[test]
pub fn test_drop_table() {
let sql = "DROP TABLE foo";
let result = ParserContext::create_with_dialect(sql, &GreptimeDbDialect {});
let mut stmts = result.unwrap();
assert_eq!(
stmts.pop().unwrap(),
Statement::DropTable(DropTable::new(ObjectName(vec![Ident::new("foo")])))
);
let sql = "DROP TABLE my_schema.foo";
let result = ParserContext::create_with_dialect(sql, &GreptimeDbDialect {});
let mut stmts = result.unwrap();
assert_eq!(
stmts.pop().unwrap(),
Statement::DropTable(DropTable::new(ObjectName(vec![
Ident::new("my_schema"),
Ident::new("foo")
])))
);
let sql = "DROP TABLE my_catalog.my_schema.foo";
let result = ParserContext::create_with_dialect(sql, &GreptimeDbDialect {});
let mut stmts = result.unwrap();
assert_eq!(
stmts.pop().unwrap(),
Statement::DropTable(DropTable::new(ObjectName(vec![
Ident::new("my_catalog"),
Ident::new("my_schema"),
Ident::new("foo")
])))
)
}
fn test_timestamp_precision(sql: &str, expected_type: ConcreteDataType) {
match ParserContext::create_with_dialect(sql, &GreptimeDbDialect {})
.unwrap()
@@ -670,27 +238,4 @@ mod tests {
ConcreteDataType::timestamp_millisecond_datatype(),
);
}
#[test]
fn test_parse_function() {
let expr =
ParserContext::parse_function("current_timestamp()", &GreptimeDbDialect {}).unwrap();
assert!(matches!(expr, Expr::Function(_)));
}
fn assert_describe_table(sql: &str) {
let stmt = ParserContext::create_with_dialect(sql, &GreptimeDbDialect {})
.unwrap()
.pop()
.unwrap();
assert!(matches!(stmt, Statement::DescribeTable(_)))
}
#[test]
fn test_parse_describe_table() {
assert_describe_table("desc table t;");
assert_describe_table("describe table t;");
assert_describe_table("desc t;");
assert_describe_table("describe t;");
}
}
+4
View File
@@ -16,7 +16,11 @@ mod alter_parser;
pub(crate) mod copy_parser;
pub(crate) mod create_parser;
pub(crate) mod delete_parser;
pub(crate) mod describe_parser;
pub(crate) mod drop_parser;
pub(crate) mod explain_parser;
pub(crate) mod insert_parser;
pub(crate) mod query_parser;
pub(crate) mod show_parser;
pub(crate) mod tql_parser;
pub(crate) mod truncate_parser;
+80
View File
@@ -0,0 +1,80 @@
// Copyright 2023 Greptime Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use snafu::{ensure, ResultExt};
use sqlparser::keywords::Keyword;
use crate::error::{self, InvalidTableNameSnafu, Result};
use crate::parser::ParserContext;
use crate::statements::describe::DescribeTable;
use crate::statements::statement::Statement;
/// DESCRIBE statement parser implementation
impl<'a> ParserContext<'a> {
pub(crate) fn parse_describe(&mut self) -> Result<Statement> {
if self.matches_keyword(Keyword::TABLE) {
let _ = self.parser.next_token();
}
self.parse_describe_table()
}
fn parse_describe_table(&mut self) -> Result<Statement> {
let table_idents =
self.parser
.parse_object_name()
.with_context(|_| error::UnexpectedSnafu {
sql: self.sql,
expected: "a table name",
actual: self.peek_token_as_string(),
})?;
ensure!(
!table_idents.0.is_empty(),
InvalidTableNameSnafu {
name: table_idents.to_string(),
}
);
Ok(Statement::DescribeTable(DescribeTable::new(table_idents)))
}
}
#[cfg(test)]
mod tests {
use sqlparser::ast::Expr;
use super::*;
use crate::dialect::GreptimeDbDialect;
#[test]
fn test_parse_function() {
let expr =
ParserContext::parse_function("current_timestamp()", &GreptimeDbDialect {}).unwrap();
assert!(matches!(expr, Expr::Function(_)));
}
fn assert_describe_table(sql: &str) {
let stmt = ParserContext::create_with_dialect(sql, &GreptimeDbDialect {})
.unwrap()
.pop()
.unwrap();
assert!(matches!(stmt, Statement::DescribeTable(_)))
}
#[test]
fn test_parse_describe_table() {
assert_describe_table("desc table t;");
assert_describe_table("describe table t;");
assert_describe_table("desc t;");
assert_describe_table("describe t;");
}
}
+91
View File
@@ -0,0 +1,91 @@
// Copyright 2023 Greptime Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use snafu::{ensure, ResultExt};
use sqlparser::keywords::Keyword;
use crate::error::{self, InvalidTableNameSnafu, Result};
use crate::parser::ParserContext;
use crate::statements::drop::DropTable;
use crate::statements::statement::Statement;
/// DROP statement parser implementation
impl<'a> ParserContext<'a> {
pub(crate) fn parse_drop(&mut self) -> Result<Statement> {
let _ = self.parser.next_token();
if !self.matches_keyword(Keyword::TABLE) {
return self.unsupported(self.peek_token_as_string());
}
let _ = self.parser.next_token();
let table_ident =
self.parser
.parse_object_name()
.with_context(|_| error::UnexpectedSnafu {
sql: self.sql,
expected: "a table name",
actual: self.peek_token_as_string(),
})?;
ensure!(
!table_ident.0.is_empty(),
InvalidTableNameSnafu {
name: table_ident.to_string()
}
);
Ok(Statement::DropTable(DropTable::new(table_ident)))
}
}
#[cfg(test)]
mod tests {
use sqlparser::ast::{Ident, ObjectName};
use super::*;
use crate::dialect::GreptimeDbDialect;
#[test]
pub fn test_drop_table() {
let sql = "DROP TABLE foo";
let result = ParserContext::create_with_dialect(sql, &GreptimeDbDialect {});
let mut stmts = result.unwrap();
assert_eq!(
stmts.pop().unwrap(),
Statement::DropTable(DropTable::new(ObjectName(vec![Ident::new("foo")])))
);
let sql = "DROP TABLE my_schema.foo";
let result = ParserContext::create_with_dialect(sql, &GreptimeDbDialect {});
let mut stmts = result.unwrap();
assert_eq!(
stmts.pop().unwrap(),
Statement::DropTable(DropTable::new(ObjectName(vec![
Ident::new("my_schema"),
Ident::new("foo")
])))
);
let sql = "DROP TABLE my_catalog.my_schema.foo";
let result = ParserContext::create_with_dialect(sql, &GreptimeDbDialect {});
let mut stmts = result.unwrap();
assert_eq!(
stmts.pop().unwrap(),
Statement::DropTable(DropTable::new(ObjectName(vec![
Ident::new("my_catalog"),
Ident::new("my_schema"),
Ident::new("foo")
])))
)
}
}
+100
View File
@@ -0,0 +1,100 @@
// Copyright 2023 Greptime Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use snafu::ResultExt;
use crate::error::{self, Result};
use crate::parser::ParserContext;
use crate::statements::explain::Explain;
use crate::statements::statement::Statement;
/// EXPLAIN statement parser implementation
impl<'a> ParserContext<'a> {
pub(crate) fn parse_explain(&mut self) -> Result<Statement> {
let explain_statement =
self.parser
.parse_explain(false)
.with_context(|_| error::UnexpectedSnafu {
sql: self.sql,
expected: "a query statement",
actual: self.peek_token_as_string(),
})?;
Ok(Statement::Explain(Explain::try_from(explain_statement)?))
}
}
#[cfg(test)]
mod tests {
use sqlparser::ast::{Query as SpQuery, Statement as SpStatement, WildcardAdditionalOptions};
use super::*;
use crate::dialect::GreptimeDbDialect;
#[test]
pub fn test_explain() {
let sql = "EXPLAIN select * from foo";
let result = ParserContext::create_with_dialect(sql, &GreptimeDbDialect {});
let stmts = result.unwrap();
assert_eq!(1, stmts.len());
let select = sqlparser::ast::Select {
distinct: None,
top: None,
projection: vec![sqlparser::ast::SelectItem::Wildcard(
WildcardAdditionalOptions::default(),
)],
into: None,
from: vec![sqlparser::ast::TableWithJoins {
relation: sqlparser::ast::TableFactor::Table {
name: sqlparser::ast::ObjectName(vec![sqlparser::ast::Ident::new("foo")]),
alias: None,
args: None,
with_hints: vec![],
},
joins: vec![],
}],
lateral_views: vec![],
selection: None,
group_by: vec![],
cluster_by: vec![],
distribute_by: vec![],
sort_by: vec![],
having: None,
qualify: None,
named_window: vec![],
};
let sp_statement = SpStatement::Query(Box::new(SpQuery {
with: None,
body: Box::new(sqlparser::ast::SetExpr::Select(Box::new(select))),
order_by: vec![],
limit: None,
offset: None,
fetch: None,
locks: vec![],
}));
let explain = Explain::try_from(SpStatement::Explain {
describe_alias: false,
analyze: false,
verbose: false,
statement: Box::new(sp_statement),
format: None,
})
.unwrap();
assert_eq!(stmts[0], Statement::Explain(explain))
}
}
+304
View File
@@ -0,0 +1,304 @@
// Copyright 2023 Greptime Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use snafu::{ensure, ResultExt};
use sqlparser::keywords::Keyword;
use sqlparser::tokenizer::Token;
use crate::error::{self, InvalidDatabaseNameSnafu, InvalidTableNameSnafu, Result};
use crate::parser::ParserContext;
use crate::statements::show::{ShowCreateTable, ShowDatabases, ShowKind, ShowTables};
use crate::statements::statement::Statement;
/// SHOW statement parser implementation
impl<'a> ParserContext<'a> {
/// Parses SHOW statements
/// todo(hl) support `show settings`/`show create`/`show users` etc.
pub(crate) fn parse_show(&mut self) -> Result<Statement> {
if self.consume_token("DATABASES") || self.consume_token("SCHEMAS") {
self.parse_show_databases()
} else if self.matches_keyword(Keyword::TABLES) {
let _ = 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<Statement> {
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 }))
}
fn parse_show_tables(&mut self) -> Result<Statement> {
let database = match self.parser.peek_token().token {
Token::EOF | Token::SemiColon => {
return Ok(Statement::ShowTables(ShowTables {
kind: ShowKind::All,
database: None,
}));
}
// SHOW TABLES [in | FROM] [DATABASE]
Token::Word(w) => match w.keyword {
Keyword::IN | Keyword::FROM => {
let _ = self.parser.next_token();
let db_name = self.parser.parse_object_name().with_context(|_| {
error::UnexpectedSnafu {
sql: self.sql,
expected: "a database name",
actual: self.peek_token_as_string(),
}
})?;
ensure!(
db_name.0.len() == 1,
InvalidDatabaseNameSnafu {
name: db_name.to_string(),
}
);
Some(db_name.to_string())
}
_ => None,
},
_ => None,
};
let kind = match self.parser.peek_token().token {
Token::EOF | Token::SemiColon => ShowKind::All,
// SHOW TABLES [WHERE | LIKE] [EXPR]
Token::Word(w) => match w.keyword {
Keyword::LIKE => {
let _ = self.parser.next_token();
ShowKind::Like(self.parser.parse_identifier().with_context(|_| {
error::UnexpectedSnafu {
sql: self.sql,
expected: "LIKE",
actual: self.peek_token_as_string(),
}
})?)
}
Keyword::WHERE => {
let _ = self.parser.next_token();
ShowKind::Where(self.parser.parse_expr().with_context(|_| {
error::UnexpectedSnafu {
sql: self.sql,
expected: "some valid expression",
actual: self.peek_token_as_string(),
}
})?)
}
_ => return self.unsupported(self.peek_token_as_string()),
},
_ => return self.unsupported(self.peek_token_as_string()),
};
Ok(Statement::ShowTables(ShowTables { kind, database }))
}
/// Parses `SHOW DATABASES` statement.
pub fn parse_show_databases(&mut self) -> Result<Statement> {
let tok = self.parser.next_token().token;
match &tok {
Token::EOF | Token::SemiColon => {
Ok(Statement::ShowDatabases(ShowDatabases::new(ShowKind::All)))
}
Token::Word(w) => match w.keyword {
Keyword::LIKE => Ok(Statement::ShowDatabases(ShowDatabases::new(
ShowKind::Like(self.parser.parse_identifier().with_context(|_| {
error::UnexpectedSnafu {
sql: self.sql,
expected: "LIKE",
actual: tok.to_string(),
}
})?),
))),
Keyword::WHERE => Ok(Statement::ShowDatabases(ShowDatabases::new(
ShowKind::Where(self.parser.parse_expr().with_context(|_| {
error::UnexpectedSnafu {
sql: self.sql,
expected: "some valid expression",
actual: self.peek_token_as_string(),
}
})?),
))),
_ => self.unsupported(self.peek_token_as_string()),
},
_ => self.unsupported(self.peek_token_as_string()),
}
}
}
#[cfg(test)]
mod tests {
use std::assert_matches::assert_matches;
use super::*;
use crate::dialect::GreptimeDbDialect;
use crate::statements::show::ShowDatabases;
#[test]
pub fn test_show_database_all() {
let sql = "SHOW DATABASES";
let result = ParserContext::create_with_dialect(sql, &GreptimeDbDialect {});
let stmts = result.unwrap();
assert_eq!(1, stmts.len());
assert_matches!(
&stmts[0],
Statement::ShowDatabases(ShowDatabases {
kind: ShowKind::All
})
);
}
#[test]
pub fn test_show_database_like() {
let sql = "SHOW DATABASES LIKE test_database";
let result = ParserContext::create_with_dialect(sql, &GreptimeDbDialect {});
let stmts = result.unwrap();
assert_eq!(1, stmts.len());
assert_matches!(
&stmts[0],
Statement::ShowDatabases(ShowDatabases {
kind: ShowKind::Like(sqlparser::ast::Ident {
value: _,
quote_style: None,
})
})
);
}
#[test]
pub fn test_show_database_where() {
let sql = "SHOW DATABASES WHERE Database LIKE '%whatever1%' OR Database LIKE '%whatever2%'";
let result = ParserContext::create_with_dialect(sql, &GreptimeDbDialect {});
let stmts = result.unwrap();
assert_eq!(1, stmts.len());
assert_matches!(
&stmts[0],
Statement::ShowDatabases(ShowDatabases {
kind: ShowKind::Where(sqlparser::ast::Expr::BinaryOp {
left: _,
right: _,
op: sqlparser::ast::BinaryOperator::Or,
})
})
);
}
#[test]
pub fn test_show_tables_all() {
let sql = "SHOW TABLES";
let result = ParserContext::create_with_dialect(sql, &GreptimeDbDialect {});
let stmts = result.unwrap();
assert_eq!(1, stmts.len());
assert_matches!(
&stmts[0],
Statement::ShowTables(ShowTables {
kind: ShowKind::All,
database: None,
})
);
}
#[test]
pub fn test_show_tables_like() {
let sql = "SHOW TABLES LIKE test_table";
let result = ParserContext::create_with_dialect(sql, &GreptimeDbDialect {});
let stmts = result.unwrap();
assert_eq!(1, stmts.len());
assert_matches!(
&stmts[0],
Statement::ShowTables(ShowTables {
kind: ShowKind::Like(sqlparser::ast::Ident {
value: _,
quote_style: None,
}),
database: None,
})
);
let sql = "SHOW TABLES in test_db LIKE test_table";
let result = ParserContext::create_with_dialect(sql, &GreptimeDbDialect {});
let stmts = result.unwrap();
assert_eq!(1, stmts.len());
assert_matches!(
&stmts[0],
Statement::ShowTables(ShowTables {
kind: ShowKind::Like(sqlparser::ast::Ident {
value: _,
quote_style: None,
}),
database: Some(_),
})
);
}
#[test]
pub fn test_show_tables_where() {
let sql = "SHOW TABLES where name like test_table";
let result = ParserContext::create_with_dialect(sql, &GreptimeDbDialect {});
let stmts = result.unwrap();
assert_eq!(1, stmts.len());
assert_matches!(
&stmts[0],
Statement::ShowTables(ShowTables {
kind: ShowKind::Where(sqlparser::ast::Expr::Like { .. }),
database: None,
})
);
let sql = "SHOW TABLES in test_db where name LIKE test_table";
let result = ParserContext::create_with_dialect(sql, &GreptimeDbDialect {});
let stmts = result.unwrap();
assert_eq!(1, stmts.len());
assert_matches!(
&stmts[0],
Statement::ShowTables(ShowTables {
kind: ShowKind::Where(sqlparser::ast::Expr::Like { .. }),
database: Some(_),
})
);
}
}