feat: create database (#451)

* feat: parsing create database statement

* feat: impl create database in datanode

* feat: supports insert into catalog.schema.table

* fix: conflicts with develop branch

* test: create database then insert and query

* fix: grpc schema provider

* feat: use CatalogManager::register_schema instead of CatalogProvide::register_schema

* refactor: revert InsertExpr catalog_name and schema_name

* fix: revert database.proto

* fix: revert client cargo

* feat: accepts schema.table as table name in sql

Co-authored-by: Lei, HUANG <mrsatangel@gmail.com>
This commit is contained in:
dennis zhuang
2022-11-11 14:15:38 +08:00
committed by GitHub
co-authored by Lei, HUANG
parent d5ae5e6afa
commit e7b4d24df5
37 changed files with 482 additions and 116 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ pub enum Error {
Unsupported { sql: String, keyword: String },
#[snafu(display(
"Unexpected token while parsing SQL statement: {}, expected: {}, found: {}, source: {}",
"Unexpected token while parsing SQL statement: {}, expected: '{}', found: {}, source: {}",
sql,
expected,
actual,
+1 -1
View File
@@ -234,7 +234,7 @@ impl<'a> ParserContext<'a> {
}
#[inline]
fn peek_token_as_string(&self) -> String {
pub(crate) fn peek_token_as_string(&self) -> String {
self.parser.peek_token().to_string()
}
+101 -15
View File
@@ -13,7 +13,9 @@ use table_engine::engine;
use crate::ast::{ColumnDef, Ident, TableConstraint, Value as SqlValue};
use crate::error::{self, InvalidTimeIndexSnafu, Result, SyntaxSnafu};
use crate::parser::ParserContext;
use crate::statements::create_table::{CreateTable, PartitionEntry, Partitions, TIME_INDEX};
use crate::statements::create::{
CreateDatabase, CreateTable, PartitionEntry, Partitions, TIME_INDEX,
};
use crate::statements::statement::Statement;
use crate::statements::{sql_data_type_to_concrete_data_type, sql_value_to_value};
@@ -26,9 +28,37 @@ static THAN: Lazy<Token> = Lazy::new(|| Token::make_keyword("THAN"));
/// Parses create [table] statement
impl<'a> ParserContext<'a> {
pub(crate) fn parse_create(&mut self) -> Result<Statement> {
self.parser
.expect_keyword(Keyword::TABLE)
.context(error::SyntaxSnafu { sql: self.sql })?;
match self.parser.peek_token() {
Token::Word(w) => match w.keyword {
Keyword::TABLE => self.parse_create_table(),
Keyword::DATABASE => self.parse_create_database(),
_ => self.unsupported(w.to_string()),
},
unexpected => self.unsupported(unexpected.to_string()),
}
}
fn parse_create_database(&mut self) -> Result<Statement> {
self.parser.next_token();
let database_name = self
.parser
.parse_object_name()
.context(error::UnexpectedSnafu {
sql: self.sql,
expected: "a database name",
actual: self.peek_token_as_string(),
})?;
Ok(Statement::CreateDatabase(CreateDatabase {
name: database_name,
}))
}
fn parse_create_table(&mut self) -> Result<Statement> {
self.parser.next_token();
let if_not_exists =
self.parser
.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
@@ -36,7 +66,12 @@ impl<'a> ParserContext<'a> {
let table_name = self
.parser
.parse_object_name()
.context(error::SyntaxSnafu { sql: self.sql })?;
.context(error::UnexpectedSnafu {
sql: self.sql,
expected: "a table name",
actual: self.peek_token_as_string(),
})?;
let (columns, constraints) = self.parse_columns()?;
let partitions = self.parse_partitions()?;
@@ -59,7 +94,7 @@ impl<'a> ParserContext<'a> {
};
validate_create(&create_table)?;
Ok(Statement::Create(create_table))
Ok(Statement::CreateTable(create_table))
}
// "PARTITION BY ..." syntax:
@@ -70,7 +105,11 @@ impl<'a> ParserContext<'a> {
}
self.parser
.expect_keywords(&[Keyword::BY, Keyword::RANGE, Keyword::COLUMNS])
.context(error::SyntaxSnafu { sql: self.sql })?;
.context(error::UnexpectedSnafu {
sql: self.sql,
expected: "BY, RANGE, COLUMNS",
actual: self.peek_token_as_string(),
})?;
let column_list = self
.parser
@@ -88,7 +127,11 @@ impl<'a> ParserContext<'a> {
fn parse_partition_entry(&mut self) -> Result<PartitionEntry> {
self.parser
.expect_keyword(Keyword::PARTITION)
.context(error::SyntaxSnafu { sql: self.sql })?;
.context(error::UnexpectedSnafu {
sql: self.sql,
expected: "PARTITION",
actual: self.peek_token_as_string(),
})?;
let name = self
.parser
@@ -128,7 +171,11 @@ impl<'a> ParserContext<'a> {
{
self.parser
.expect_token(&Token::LParen)
.context(error::SyntaxSnafu { sql: self.sql })?;
.context(error::UnexpectedSnafu {
sql: self.sql,
expected: "(",
actual: self.peek_token_as_string(),
})?;
let mut values = vec![];
while self.parser.peek_token() != Token::RParen {
@@ -140,7 +187,12 @@ impl<'a> ParserContext<'a> {
self.parser
.expect_token(&Token::RParen)
.context(error::SyntaxSnafu { sql: self.sql })?;
.context(error::UnexpectedSnafu {
sql: self.sql,
expected: ")",
actual: self.peek_token_as_string(),
})?;
Ok(values)
}
@@ -196,7 +248,11 @@ impl<'a> ParserContext<'a> {
Token::Word(w) if w.keyword == Keyword::PRIMARY => {
self.parser
.expect_keyword(Keyword::KEY)
.context(error::SyntaxSnafu { sql: self.sql })?;
.context(error::UnexpectedSnafu {
sql: self.sql,
expected: "KEY",
actual: self.peek_token_as_string(),
})?;
let columns = self
.parser
.parse_parenthesized_column_list(Mandatory)
@@ -210,7 +266,12 @@ impl<'a> ParserContext<'a> {
Token::Word(w) if w.keyword == Keyword::TIME => {
self.parser
.expect_keyword(Keyword::INDEX)
.context(error::SyntaxSnafu { sql: self.sql })?;
.context(error::UnexpectedSnafu {
sql: self.sql,
expected: "INDEX",
actual: self.peek_token_as_string(),
})?;
let columns = self
.parser
.parse_parenthesized_column_list(Mandatory)
@@ -248,7 +309,11 @@ impl<'a> ParserContext<'a> {
self.parser
.expect_token(&Token::Eq)
.context(error::SyntaxSnafu { sql: self.sql })?;
.context(error::UnexpectedSnafu {
sql: self.sql,
expected: "=",
actual: self.peek_token_as_string(),
})?;
match self.parser.next_token() {
Token::Word(w) => Ok(w.value),
@@ -420,6 +485,27 @@ mod tests {
use super::*;
#[test]
fn test_parse_create_database() {
let sql = "create database";
let result = ParserContext::create_with_dialect(sql, &GenericDialect {});
assert!(result
.unwrap_err()
.to_string()
.contains("Unexpected token while parsing SQL statement"));
let sql = "create database prometheus";
let stmts = ParserContext::create_with_dialect(sql, &GenericDialect {}).unwrap();
assert_eq!(1, stmts.len());
match &stmts[0] {
Statement::CreateDatabase(c) => {
assert_eq!(c.name.to_string(), "prometheus");
}
_ => unreachable!(),
}
}
#[test]
fn test_validate_create() {
let sql = r"
@@ -555,7 +641,7 @@ ENGINE=mito";
let result = ParserContext::create_with_dialect(sql, &GenericDialect {}).unwrap();
assert_eq!(result.len(), 1);
match &result[0] {
Statement::Create(c) => {
Statement::CreateTable(c) => {
assert!(c.partitions.is_some());
let partitions = c.partitions.as_ref().unwrap();
@@ -670,7 +756,7 @@ ENGINE=mito";
let result = ParserContext::create_with_dialect(sql, &GenericDialect {}).unwrap();
assert_eq!(1, result.len());
match &result[0] {
Statement::Create(c) => {
Statement::CreateTable(c) => {
assert!(!c.if_not_exists);
assert_eq!("demo", c.name.to_string());
assert_eq!("mito", c.engine);
+7 -2
View File
@@ -1,5 +1,5 @@
pub mod alter;
pub mod create_table;
pub mod create;
pub mod insert;
pub mod query;
pub mod show;
@@ -32,6 +32,11 @@ pub fn table_idents_to_full_name(obj_name: &ObjectName) -> Result<(String, Strin
DEFAULT_SCHEMA_NAME.to_string(),
table.value.clone(),
)),
[schema, table] => Ok((
DEFAULT_CATALOG_NAME.to_string(),
schema.value.clone(),
table.value.clone(),
)),
[catalog, schema, table] => Ok((
catalog.value.clone(),
schema.value.clone(),
@@ -39,7 +44,7 @@ pub fn table_idents_to_full_name(obj_name: &ObjectName) -> Result<(String, Strin
)),
_ => error::InvalidSqlSnafu {
msg: format!(
"expect table name to be <catalog>.<schema>.<table> or <table>, actual: {}",
"expect table name to be <catalog>.<schema>.<table>, <schema>.<table> or <table>, actual: {}",
obj_name
),
}
@@ -29,3 +29,8 @@ pub struct PartitionEntry {
pub name: Ident,
pub value_list: Vec<SqlValue>,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct CreateDatabase {
pub name: ObjectName,
}
+3 -5
View File
@@ -3,6 +3,7 @@ use sqlparser::parser::ParserError;
use crate::ast::{Expr, Value};
use crate::error::{self, Result};
use crate::statements::table_idents_to_full_name;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Insert {
@@ -11,12 +12,9 @@ pub struct Insert {
}
impl Insert {
pub fn table_name(&self) -> String {
pub fn full_table_name(&self) -> Result<(String, String, String)> {
match &self.inner {
Statement::Insert { table_name, .. } => {
// FIXME(dennis): table_name may be in the form of "catalog.schema.table"
table_name.to_string()
}
Statement::Insert { table_name, .. } => table_idents_to_full_name(table_name),
_ => unreachable!(),
}
}
+7 -3
View File
@@ -2,7 +2,7 @@ use sqlparser::ast::Statement as SpStatement;
use sqlparser::parser::ParserError;
use crate::statements::alter::AlterTable;
use crate::statements::create_table::CreateTable;
use crate::statements::create::{CreateDatabase, CreateTable};
use crate::statements::insert::Insert;
use crate::statements::query::Query;
use crate::statements::show::{ShowCreateTable, ShowDatabases, ShowTables};
@@ -15,7 +15,9 @@ pub enum Statement {
// Insert
Insert(Box<Insert>),
/// CREATE TABLE
Create(CreateTable),
CreateTable(CreateTable),
// CREATE DATABASE
CreateDatabase(CreateDatabase),
/// ALTER TABLE
Alter(AlterTable),
// Databases.
@@ -43,7 +45,9 @@ impl TryFrom<Statement> for SpStatement {
)),
Statement::Query(s) => Ok(SpStatement::Query(Box::new(s.inner))),
Statement::Insert(i) => Ok(i.inner),
Statement::Create(_) | Statement::Alter(_) => unimplemented!(),
Statement::CreateDatabase(_) | Statement::CreateTable(_) | Statement::Alter(_) => {
unimplemented!()
}
}
}
}