diff --git a/src/api/greptime/v1/admin.proto b/src/api/greptime/v1/admin.proto index 9c2c95ecdf..3f253cde0f 100644 --- a/src/api/greptime/v1/admin.proto +++ b/src/api/greptime/v1/admin.proto @@ -20,6 +20,7 @@ message AdminExpr { CreateExpr create = 2; AlterExpr alter = 3; CreateDatabaseExpr create_database = 4; + DropTableExpr drop_table = 5; } } @@ -55,6 +56,12 @@ message AlterExpr { } } +message DropTableExpr { + string catalog_name = 1; + string schema_name = 2; + string table_name = 3; +} + message AddColumns { repeated AddColumn add_columns = 1; } diff --git a/src/catalog/src/lib.rs b/src/catalog/src/lib.rs index 1490d6a67b..fc7bb42b03 100644 --- a/src/catalog/src/lib.rs +++ b/src/catalog/src/lib.rs @@ -146,7 +146,6 @@ pub struct DeregisterTableRequest { pub catalog: String, pub schema: String, pub table_name: String, - pub table_id: TableId, } #[derive(Debug, Clone)] diff --git a/src/catalog/src/local/memory.rs b/src/catalog/src/local/memory.rs index a870c84aa3..fb41058ad0 100644 --- a/src/catalog/src/local/memory.rs +++ b/src/catalog/src/local/memory.rs @@ -393,7 +393,6 @@ mod tests { catalog: DEFAULT_CATALOG_NAME.to_string(), schema: DEFAULT_SCHEMA_NAME.to_string(), table_name: "numbers".to_string(), - table_id: 2333, }; catalog .deregister_table(deregister_table_req) diff --git a/src/catalog/tests/mock.rs b/src/catalog/tests/mock.rs index 067abc7ca0..01aec6e2f8 100644 --- a/src/catalog/tests/mock.rs +++ b/src/catalog/tests/mock.rs @@ -217,7 +217,7 @@ impl TableEngine for MockTableEngine { &self, _ctx: &EngineContext, _request: DropTableRequest, - ) -> table::Result<()> { + ) -> table::Result { unimplemented!() } } diff --git a/src/client/src/admin.rs b/src/client/src/admin.rs index d872dd41d2..f70aea0356 100644 --- a/src/client/src/admin.rs +++ b/src/client/src/admin.rs @@ -58,7 +58,19 @@ impl Admin { header: Some(header), expr: Some(admin_expr::Expr::Alter(expr)), }; - Ok(self.do_requests(vec![expr]).await?.remove(0)) + self.do_request(expr).await + } + + pub async fn drop_table(&self, expr: DropTableExpr) -> Result { + let header = ExprHeader { + version: PROTOCOL_VERSION, + }; + let expr = AdminExpr { + header: Some(header), + expr: Some(admin_expr::Expr::DropTable(expr)), + }; + + self.do_request(expr).await } /// Invariants: the lengths of input vec (`Vec`) and output vec (`Vec`) are equal. diff --git a/src/datanode/src/error.rs b/src/datanode/src/error.rs index a15ee9e385..181fa3f43f 100644 --- a/src/datanode/src/error.rs +++ b/src/datanode/src/error.rs @@ -73,6 +73,13 @@ pub enum Error { source: TableError, }, + #[snafu(display("Failed to drop table {}, source: {}", table_name, source))] + DropTable { + table_name: String, + #[snafu(backtrace)] + source: BoxedError, + }, + #[snafu(display("Table not found: {}", table_name))] TableNotFound { table_name: String }, @@ -298,6 +305,7 @@ impl ErrorExt for Error { Error::CreateTable { source, .. } | Error::GetTable { source, .. } | Error::AlterTable { source, .. } => source.status_code(), + Error::DropTable { source, .. } => source.status_code(), Error::Insert { source, .. } => source.status_code(), diff --git a/src/datanode/src/instance/grpc.rs b/src/datanode/src/instance/grpc.rs index ee7c4f8c0e..da7d9f4136 100644 --- a/src/datanode/src/instance/grpc.rs +++ b/src/datanode/src/instance/grpc.rs @@ -195,6 +195,9 @@ impl GrpcAdminHandler for Instance { Some(admin_expr::Expr::CreateDatabase(create_database_expr)) => { self.execute_create_database(create_database_expr).await } + Some(admin_expr::Expr::DropTable(drop_table_expr)) => { + self.handle_drop_table(drop_table_expr).await + } other => { return servers::error::NotSupportedSnafu { feat: format!("{:?}", other), diff --git a/src/datanode/src/instance/sql.rs b/src/datanode/src/instance/sql.rs index 8f8b263a31..5737f94a60 100644 --- a/src/datanode/src/instance/sql.rs +++ b/src/datanode/src/instance/sql.rs @@ -107,6 +107,10 @@ impl Instance { let req = self.sql_handler.alter_to_request(alter_table)?; self.sql_handler.execute(SqlRequest::Alter(req)).await } + Statement::DropTable(drop_table) => { + let req = self.sql_handler.drop_table_to_request(drop_table); + self.sql_handler.execute(SqlRequest::DropTable(req)).await + } Statement::ShowDatabases(stmt) => { self.sql_handler .execute(SqlRequest::ShowDatabases(stmt)) diff --git a/src/datanode/src/server/grpc/ddl.rs b/src/datanode/src/server/grpc/ddl.rs index d8133657df..5629e0fa9d 100644 --- a/src/datanode/src/server/grpc/ddl.rs +++ b/src/datanode/src/server/grpc/ddl.rs @@ -13,13 +13,14 @@ // limitations under the License. use api::result::AdminResultBuilder; -use api::v1::{AdminResult, AlterExpr, CreateExpr}; +use api::v1::{AdminResult, AlterExpr, CreateExpr, DropTableExpr}; use common_error::prelude::{ErrorExt, StatusCode}; use common_grpc_expr::{alter_expr_to_request, create_expr_to_request}; use common_query::Output; use common_telemetry::{error, info}; use futures::TryFutureExt; use snafu::prelude::*; +use table::requests::DropTableRequest; use crate::error::{AlterExprToRequestSnafu, BumpTableIdSnafu, CreateExprToRequestSnafu}; use crate::instance::Instance; @@ -116,6 +117,26 @@ impl Instance { .build(), } } + + pub(crate) async fn handle_drop_table(&self, expr: DropTableExpr) -> AdminResult { + let req = DropTableRequest { + catalog_name: expr.catalog_name, + schema_name: expr.schema_name, + table_name: expr.table_name, + }; + let result = self.sql_handler().execute(SqlRequest::DropTable(req)).await; + match result { + Ok(Output::AffectedRows(rows)) => AdminResultBuilder::default() + .status_code(StatusCode::Success as u32) + .mutate_result(rows as _, 0) + .build(), + Ok(Output::Stream(_)) | Ok(Output::RecordBatches(_)) => unreachable!(), + Err(err) => AdminResultBuilder::default() + .status_code(err.status_code() as u32) + .err_msg(err.to_string()) + .build(), + } + } } #[cfg(test)] diff --git a/src/datanode/src/sql.rs b/src/datanode/src/sql.rs index 2817fda387..1c097d61d4 100644 --- a/src/datanode/src/sql.rs +++ b/src/datanode/src/sql.rs @@ -16,6 +16,7 @@ use catalog::CatalogManagerRef; use common_query::Output; +use common_telemetry::error; use query::query_engine::QueryEngineRef; use query::sql::{describe_table, explain, show_databases, show_tables}; use snafu::{OptionExt, ResultExt}; @@ -26,10 +27,11 @@ use table::engine::{EngineContext, TableEngineRef, TableReference}; use table::requests::*; use table::TableRef; -use crate::error::{self, GetTableSnafu, Result, TableNotFoundSnafu}; +use crate::error::{ExecuteSqlSnafu, GetTableSnafu, Result, TableNotFoundSnafu}; mod alter; mod create; +mod drop_table; mod insert; #[derive(Debug)] @@ -38,6 +40,7 @@ pub enum SqlRequest { CreateTable(CreateTableRequest), CreateDatabase(CreateDatabaseRequest), Alter(AlterTableRequest), + DropTable(DropTableRequest), ShowDatabases(ShowDatabases), ShowTables(ShowTables), DescribeTable(DescribeTable), @@ -65,24 +68,29 @@ impl SqlHandler { } pub async fn execute(&self, request: SqlRequest) -> Result { - match request { + let result = match request { SqlRequest::Insert(req) => self.insert(req).await, SqlRequest::CreateTable(req) => self.create_table(req).await, SqlRequest::CreateDatabase(req) => self.create_database(req).await, SqlRequest::Alter(req) => self.alter(req).await, + SqlRequest::DropTable(req) => self.drop_table(req).await, SqlRequest::ShowDatabases(stmt) => { - show_databases(stmt, self.catalog_manager.clone()).context(error::ExecuteSqlSnafu) + show_databases(stmt, self.catalog_manager.clone()).context(ExecuteSqlSnafu) } SqlRequest::ShowTables(stmt) => { - show_tables(stmt, self.catalog_manager.clone()).context(error::ExecuteSqlSnafu) + show_tables(stmt, self.catalog_manager.clone()).context(ExecuteSqlSnafu) } SqlRequest::DescribeTable(stmt) => { - describe_table(stmt, self.catalog_manager.clone()).context(error::ExecuteSqlSnafu) + describe_table(stmt, self.catalog_manager.clone()).context(ExecuteSqlSnafu) } SqlRequest::Explain(stmt) => explain(stmt, self.query_engine.clone()) .await - .context(error::ExecuteSqlSnafu), + .context(ExecuteSqlSnafu), + }; + if let Err(e) = &result { + error!("Datanode execution error: {:?}", e); } + result } pub(crate) fn get_table<'a>(&self, table_ref: &'a TableReference) -> Result { diff --git a/src/datanode/src/sql/drop_table.rs b/src/datanode/src/sql/drop_table.rs new file mode 100644 index 0000000000..4a56b669c9 --- /dev/null +++ b/src/datanode/src/sql/drop_table.rs @@ -0,0 +1,71 @@ +// Copyright 2022 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 catalog::DeregisterTableRequest; +use common_error::prelude::BoxedError; +use common_query::Output; +use common_telemetry::info; +use snafu::ResultExt; +use sql::statements::drop::DropTable; +use table::engine::{EngineContext, TableReference}; +use table::requests::DropTableRequest; + +use crate::error::{self, Result}; +use crate::sql::SqlHandler; + +impl SqlHandler { + pub async fn drop_table(&self, req: DropTableRequest) -> Result { + let deregister_table_req = DeregisterTableRequest { + catalog: req.catalog_name.clone(), + schema: req.schema_name.clone(), + table_name: req.table_name.clone(), + }; + + let table_reference = TableReference { + catalog: &req.catalog_name, + schema: &req.schema_name, + table: &req.table_name, + }; + let table_full_name = table_reference.to_string(); + + self.catalog_manager + .deregister_table(deregister_table_req) + .await + .map_err(BoxedError::new) + .context(error::DropTableSnafu { + table_name: table_full_name.clone(), + })?; + + let ctx = EngineContext {}; + self.table_engine() + .drop_table(&ctx, req) + .await + .map_err(BoxedError::new) + .context(error::DropTableSnafu { + table_name: table_full_name.clone(), + })?; + + info!("Successfully dropped table: {}", table_full_name); + + Ok(Output::AffectedRows(1)) + } + + pub fn drop_table_to_request(&self, drop_table: DropTable) -> DropTableRequest { + DropTableRequest { + catalog_name: drop_table.catalog_name, + schema_name: drop_table.schema_name, + table_name: drop_table.table_name, + } + } +} diff --git a/src/frontend/src/error.rs b/src/frontend/src/error.rs index 5be2009877..9b6275c7bf 100644 --- a/src/frontend/src/error.rs +++ b/src/frontend/src/error.rs @@ -250,6 +250,12 @@ pub enum Error { source: client::Error, }, + #[snafu(display("Failed to drop table, source: {}", source))] + DropTable { + #[snafu(backtrace)] + source: client::Error, + }, + #[snafu(display("Failed to insert values to table, source: {}", source))] Insert { #[snafu(backtrace)] @@ -509,19 +515,20 @@ impl ErrorExt for Error { Error::BumpTableId { source, .. } => source.status_code(), Error::SchemaNotFound { .. } => StatusCode::InvalidArguments, Error::CatalogNotFound { .. } => StatusCode::InvalidArguments, - Error::CreateTable { source, .. } => source.status_code(), - Error::AlterTable { source, .. } => source.status_code(), - Error::Insert { source, .. } => source.status_code(), + Error::CreateTable { source, .. } + | Error::AlterTable { source, .. } + | Error::DropTable { source } + | Error::Select { source, .. } + | Error::CreateDatabase { source, .. } + | Error::CreateTableOnInsertion { source, .. } + | Error::AlterTableOnInsertion { source, .. } + | Error::Insert { source, .. } => source.status_code(), Error::BuildCreateExprOnInsertion { source, .. } => source.status_code(), - Error::CreateTableOnInsertion { source, .. } => source.status_code(), - Error::AlterTableOnInsertion { source, .. } => source.status_code(), - Error::Select { source, .. } => source.status_code(), Error::FindNewColumnsOnInsertion { source, .. } => source.status_code(), Error::DeserializeInsertBatch { source, .. } => source.status_code(), Error::PrimaryKeyNotFound { .. } => StatusCode::InvalidArguments, Error::ExecuteSql { source, .. } => source.status_code(), Error::InsertBatchToRequest { source, .. } => source.status_code(), - Error::CreateDatabase { source, .. } => source.status_code(), Error::CollectRecordbatchStream { source } | Error::CreateRecordbatches { source } => { source.status_code() } diff --git a/src/frontend/src/instance.rs b/src/frontend/src/instance.rs index 4d82d4d601..25a8e99d2a 100644 --- a/src/frontend/src/instance.rs +++ b/src/frontend/src/instance.rs @@ -25,7 +25,8 @@ use api::v1::alter_expr::Kind; use api::v1::object_expr::Expr; use api::v1::{ admin_expr, select_expr, AddColumns, AdminExpr, AdminResult, AlterExpr, Column, - CreateDatabaseExpr, CreateExpr, InsertExpr, ObjectExpr, ObjectResult as GrpcObjectResult, + CreateDatabaseExpr, CreateExpr, DropTableExpr, InsertExpr, ObjectExpr, + ObjectResult as GrpcObjectResult, }; use async_trait::async_trait; use catalog::remote::MetaKvBackend; @@ -58,8 +59,9 @@ use crate::catalog::FrontendCatalogManager; use crate::datanode::DatanodeClients; use crate::error::{ self, AlterTableOnInsertionSnafu, AlterTableSnafu, CatalogNotFoundSnafu, CatalogSnafu, - CreateDatabaseSnafu, CreateTableSnafu, FindNewColumnsOnInsertionSnafu, InsertSnafu, - MissingMetasrvOptsSnafu, Result, SchemaNotFoundSnafu, SelectSnafu, + CreateDatabaseSnafu, CreateTableSnafu, DropTableSnafu, FindNewColumnsOnInsertionSnafu, + InsertSnafu, MissingMetasrvOptsSnafu, Result, SchemaNotFoundSnafu, SelectSnafu, + UnsupportedExprSnafu, }; use crate::expr_factory::{CreateExprFactoryRef, DefaultCreateExprFactory}; use crate::frontend::FrontendOptions; @@ -278,6 +280,23 @@ impl Instance { } } + /// Handle drop table expr + pub async fn handle_drop_table(&self, expr: DropTableExpr) -> Result { + match self.mode { + Mode::Standalone => self + .admin(&expr.schema_name) + .drop_table(expr) + .await + .and_then(admin_result_to_output) + .context(DropTableSnafu), + // TODO(ruihang): support drop table in distributed mode + Mode::Distributed => UnsupportedExprSnafu { + name: "Distributed DROP TABLE", + } + .fail(), + } + } + /// Handle explain expr pub async fn handle_explain(&self, sql: &str, explain_stmt: Explain) -> Result { if let Some(dist_instance) = &self.dist_instance { @@ -615,6 +634,17 @@ impl SqlQueryHandler for Instance { .await .map_err(BoxedError::new) .context(server_error::ExecuteQuerySnafu { query }), + Statement::DropTable(drop_stmt) => { + let expr = DropTableExpr { + catalog_name: drop_stmt.catalog_name, + schema_name: drop_stmt.schema_name, + table_name: drop_stmt.table_name, + }; + self.handle_drop_table(expr) + .await + .map_err(BoxedError::new) + .context(server_error::ExecuteQuerySnafu { query }) + } Statement::Explain(explain_stmt) => self .handle_explain(query, explain_stmt) .await @@ -726,6 +756,7 @@ fn get_schema_name(expr: &AdminExpr) -> &str { Some(admin_expr::Expr::Create(expr)) => expr.schema_name.as_deref(), Some(admin_expr::Expr::Alter(expr)) => expr.schema_name.as_deref(), Some(admin_expr::Expr::CreateDatabase(_)) | None => Some(DEFAULT_SCHEMA_NAME), + Some(admin_expr::Expr::DropTable(expr)) => Some(expr.schema_name.as_ref()), }; schema_name.unwrap_or(DEFAULT_SCHEMA_NAME) } diff --git a/src/mito/src/engine.rs b/src/mito/src/engine.rs index 627ef5bdf4..1264c4f2c0 100644 --- a/src/mito/src/engine.rs +++ b/src/mito/src/engine.rs @@ -123,14 +123,14 @@ impl TableEngine for MitoEngine { async fn drop_table( &self, _ctx: &EngineContext, - _request: DropTableRequest, - ) -> TableResult<()> { - unimplemented!(); + request: DropTableRequest, + ) -> TableResult { + Ok(self.inner.drop_table(request).await?) } } struct MitoEngineInner { - /// All tables opened by the engine. + /// All tables opened by the engine. Map key is formatted [TableReference]. /// /// Writing to `tables` should also hold the `table_mutex`. tables: RwLock>, @@ -464,6 +464,22 @@ impl MitoEngineInner { .context(error::AlterTableSnafu { table_name })?; Ok(table) } + + /// Drop table. Returns whether a table is dropped (true) or not exist (false). + async fn drop_table(&self, req: DropTableRequest) -> Result { + let table_reference = TableReference { + catalog: &req.catalog_name, + schema: &req.schema_name, + table: &req.table_name, + }; + // todo(ruihang): reclaim persisted data + Ok(self + .tables + .write() + .unwrap() + .remove(&table_reference.to_string()) + .is_some()) + } } impl MitoEngineInner { @@ -961,4 +977,69 @@ mod tests { assert_eq!(new_schema.timestamp_column(), old_schema.timestamp_column()); assert_eq!(new_schema.version(), old_schema.version() + 1); } + + #[tokio::test] + async fn test_drop_table() { + common_telemetry::init_default_ut_logging(); + let ctx = EngineContext::default(); + + let (_engine, table_engine, table, _object_store, _dir) = + test_util::setup_mock_engine_and_table().await; + let engine_ctx = EngineContext {}; + + let table_info = table.table_info(); + let table_reference = TableReference { + catalog: DEFAULT_CATALOG_NAME, + schema: DEFAULT_SCHEMA_NAME, + table: &table_info.name, + }; + + let create_table_request = CreateTableRequest { + id: 1, + catalog_name: DEFAULT_CATALOG_NAME.to_string(), + schema_name: DEFAULT_SCHEMA_NAME.to_string(), + table_name: table_info.name.to_string(), + schema: table_info.meta.schema.clone(), + create_if_not_exists: true, + desc: None, + primary_key_indices: Vec::default(), + table_options: HashMap::new(), + region_numbers: vec![0], + }; + + let created_table = table_engine + .create_table(&ctx, create_table_request) + .await + .unwrap(); + assert_eq!(table_info, created_table.table_info()); + assert!(table_engine.table_exists(&engine_ctx, &table_reference)); + + let drop_table_request = DropTableRequest { + catalog_name: table_reference.catalog.to_string(), + schema_name: table_reference.schema.to_string(), + table_name: table_reference.table.to_string(), + }; + let table_dropped = table_engine + .drop_table(&engine_ctx, drop_table_request) + .await + .unwrap(); + assert!(table_dropped); + assert!(!table_engine.table_exists(&engine_ctx, &table_reference)); + + // should be able to re-create + let request = CreateTableRequest { + id: 2, + catalog_name: DEFAULT_CATALOG_NAME.to_string(), + schema_name: DEFAULT_SCHEMA_NAME.to_string(), + table_name: table_info.name.to_string(), + schema: table_info.meta.schema.clone(), + create_if_not_exists: false, + desc: None, + primary_key_indices: Vec::default(), + table_options: HashMap::new(), + region_numbers: vec![0], + }; + table_engine.create_table(&ctx, request).await.unwrap(); + assert!(table_engine.table_exists(&engine_ctx, &table_reference)); + } } diff --git a/src/query/src/datafusion/planner.rs b/src/query/src/datafusion/planner.rs index b3ae490fd4..0f3d00ea2a 100644 --- a/src/query/src/datafusion/planner.rs +++ b/src/query/src/datafusion/planner.rs @@ -84,7 +84,8 @@ where | Statement::CreateTable(_) | Statement::CreateDatabase(_) | Statement::Alter(_) - | Statement::Insert(_) => unreachable!(), + | Statement::Insert(_) + | Statement::DropTable(_) => unreachable!(), } } } diff --git a/src/sql/src/parser.rs b/src/sql/src/parser.rs index 11e29b26a7..4665ec6243 100644 --- a/src/sql/src/parser.rs +++ b/src/sql/src/parser.rs @@ -22,6 +22,7 @@ use crate::error::{ self, InvalidDatabaseNameSnafu, InvalidTableNameSnafu, Result, SyntaxSnafu, TokenizerSnafu, }; 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; @@ -99,6 +100,8 @@ impl<'a> ParserContext<'a> { Keyword::ALTER => self.parse_alter(), + Keyword::DROP => self.parse_drop(), + // todo(hl) support more statements. _ => self.unsupported(self.peek_token_as_string()), } @@ -271,6 +274,36 @@ impl<'a> ParserContext<'a> { Ok(Statement::Explain(Explain::try_from(explain_statement)?)) } + fn parse_drop(&mut self) -> Result { + self.parser.next_token(); + if !self.matches_keyword(Keyword::TABLE) { + return self.unsupported(self.peek_token_as_string()); + } + 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() + } + ); + + let (catalog_name, schema_name, table_name) = table_idents_to_full_name(&table_ident)?; + Ok(Statement::DropTable(DropTable { + catalog_name, + schema_name, + table_name, + })) + } + // Report unexpected token pub(crate) fn expected(&self, expected: &str, found: Token) -> Result { Err(ParserError::ParserError(format!( @@ -338,6 +371,7 @@ impl<'a> ParserContext<'a> { mod tests { use std::assert_matches::assert_matches; + use common_catalog::consts::{DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME}; use sqlparser::ast::{Query as SpQuery, Statement as SpStatement}; use sqlparser::dialect::GenericDialect; @@ -532,4 +566,43 @@ mod tests { 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, &GenericDialect {}); + let mut stmts = result.unwrap(); + assert_eq!( + stmts.pop().unwrap(), + Statement::DropTable(DropTable { + catalog_name: DEFAULT_CATALOG_NAME.to_string(), + schema_name: DEFAULT_SCHEMA_NAME.to_string(), + table_name: "foo".to_string() + }) + ); + + let sql = "DROP TABLE my_schema.foo"; + let result = ParserContext::create_with_dialect(sql, &GenericDialect {}); + let mut stmts = result.unwrap(); + assert_eq!( + stmts.pop().unwrap(), + Statement::DropTable(DropTable { + catalog_name: DEFAULT_CATALOG_NAME.to_string(), + schema_name: "my_schema".to_string(), + table_name: "foo".to_string() + }) + ); + + let sql = "DROP TABLE my_catalog.my_schema.foo"; + let result = ParserContext::create_with_dialect(sql, &GenericDialect {}); + let mut stmts = result.unwrap(); + assert_eq!( + stmts.pop().unwrap(), + Statement::DropTable(DropTable { + catalog_name: "my_catalog".to_string(), + schema_name: "my_schema".to_string(), + table_name: "foo".to_string() + }) + ) + } } diff --git a/src/sql/src/statements.rs b/src/sql/src/statements.rs index c34d07158e..89b9831b5d 100644 --- a/src/sql/src/statements.rs +++ b/src/sql/src/statements.rs @@ -15,6 +15,7 @@ pub mod alter; pub mod create; pub mod describe; +pub mod drop; pub mod explain; pub mod insert; pub mod query; diff --git a/src/sql/src/statements/drop.rs b/src/sql/src/statements/drop.rs new file mode 100644 index 0000000000..5ea56d6e4c --- /dev/null +++ b/src/sql/src/statements/drop.rs @@ -0,0 +1,32 @@ +// Copyright 2022 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. + +/// DROP TABLE statement. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DropTable { + pub catalog_name: String, + pub schema_name: String, + pub table_name: String, +} + +impl DropTable { + /// Creates a statement for `DROP TABLE` + pub fn new(catalog_name: String, schema_name: String, table_name: String) -> Self { + DropTable { + catalog_name, + schema_name, + table_name, + } + } +} diff --git a/src/sql/src/statements/statement.rs b/src/sql/src/statements/statement.rs index c3e734dc9d..a350f5e22d 100644 --- a/src/sql/src/statements/statement.rs +++ b/src/sql/src/statements/statement.rs @@ -18,6 +18,7 @@ use sqlparser::parser::ParserError; use crate::statements::alter::AlterTable; use crate::statements::create::{CreateDatabase, CreateTable}; use crate::statements::describe::DescribeTable; +use crate::statements::drop::DropTable; use crate::statements::explain::Explain; use crate::statements::insert::Insert; use crate::statements::query::Query; @@ -33,6 +34,8 @@ pub enum Statement { Insert(Box), /// CREATE TABLE CreateTable(CreateTable), + // DROP TABLE + DropTable(DropTable), // CREATE DATABASE CreateDatabase(CreateDatabase), /// ALTER TABLE @@ -67,6 +70,9 @@ impl TryFrom for SpStatement { Statement::DescribeTable(_) => Err(ParserError::ParserError( "sqlparser does not support DESCRIBE TABLE query.".to_string(), )), + Statement::DropTable(_) => Err(ParserError::ParserError( + "sqlparser does not support DROP TABLE query.".to_string(), + )), Statement::Query(s) => Ok(SpStatement::Query(Box::new(s.inner))), Statement::Insert(i) => Ok(i.inner), Statement::CreateDatabase(_) | Statement::CreateTable(_) | Statement::Alter(_) => { diff --git a/src/table/src/engine.rs b/src/table/src/engine.rs index d2983e547b..0929e29b45 100644 --- a/src/table/src/engine.rs +++ b/src/table/src/engine.rs @@ -74,8 +74,8 @@ pub trait TableEngine: Send + Sync { /// Returns true when the given table is exists. fn table_exists<'a>(&self, ctx: &EngineContext, table_ref: &'a TableReference) -> bool; - /// Drops the given table. - async fn drop_table(&self, ctx: &EngineContext, request: DropTableRequest) -> Result<()>; + /// Drops the given table. Return true if the table is dropped, or false if the table doesn't exist. + async fn drop_table(&self, ctx: &EngineContext, request: DropTableRequest) -> Result; } pub type TableEngineRef = Arc; diff --git a/src/table/src/requests.rs b/src/table/src/requests.rs index bc0b1a8e34..9d5e877aad 100644 --- a/src/table/src/requests.rs +++ b/src/table/src/requests.rs @@ -84,4 +84,8 @@ pub enum AlterKind { /// Drop table request #[derive(Debug)] -pub struct DropTableRequest {} +pub struct DropTableRequest { + pub catalog_name: String, + pub schema_name: String, + pub table_name: String, +} diff --git a/src/table/src/test_util/mock_engine.rs b/src/table/src/test_util/mock_engine.rs index af14a6112b..2b19b1889a 100644 --- a/src/table/src/test_util/mock_engine.rs +++ b/src/table/src/test_util/mock_engine.rs @@ -97,7 +97,7 @@ impl TableEngine for MockTableEngine { unimplemented!() } - async fn drop_table(&self, _ctx: &EngineContext, _request: DropTableRequest) -> Result<()> { + async fn drop_table(&self, _ctx: &EngineContext, _request: DropTableRequest) -> Result { unimplemented!() } } diff --git a/tests/cases/standalone/basic.result b/tests/cases/standalone/basic.result index ebd6a817bd..229da9c61f 100644 --- a/tests/cases/standalone/basic.result +++ b/tests/cases/standalone/basic.result @@ -56,5 +56,5 @@ SELECT idc, avg(memory_util) FROM system_metrics GROUP BY idc ORDER BY idc; DROP TABLE system_metrics; -Failed to execute, error: Datanode { code: 1001, msg: "Failed to execute sql, source: Cannot parse SQL, source: SQL statement is not supported: DROP TABLE system_metrics;, keyword: DROP" } +MutateResult { success: 1, failure: 0 } diff --git a/tests/cases/standalone/select/dummy.output b/tests/cases/standalone/select/dummy.output deleted file mode 100644 index e0ac9dc3f0..0000000000 --- a/tests/cases/standalone/select/dummy.output +++ /dev/null @@ -1,36 +0,0 @@ -select 1; - -+--------------------------+ -| Int64(1), #Field, #Int64 | -+--------------------------+ -| 1 | -+--------------------------+ - -select 2 + 3; - -+----------------------------------------+ -| Int64(2) Plus Int64(3), #Field, #Int64 | -+----------------------------------------+ -| 5 | -+----------------------------------------+ - -select 4 + 0.5; - -+----------------------------------------------+ -| Int64(4) Plus Float64(0.5), #Field, #Float64 | -+----------------------------------------------+ -| 4.5 | -+----------------------------------------------+ - -select "a"; - -Failed to execute, error: Datanode { code: 3000, msg: "Failed to execute sql, source: Cannot plan SQL: SELECT \"a\", source: Error during planning: Invalid identifier '#a' for schema fields:[], metadata:{}" } - -select "A"; - -Failed to execute, error: Datanode { code: 3000, msg: "Failed to execute sql, source: Cannot plan SQL: SELECT \"A\", source: Error during planning: Invalid identifier '#A' for schema fields:[], metadata:{}" } - -select * where "a" = "A"; - -Failed to execute, error: Datanode { code: 3000, msg: "Failed to execute sql, source: Cannot plan SQL: SELECT * WHERE \"a\" = \"A\", source: Error during planning: Invalid identifier '#a' for schema fields:[], metadata:{}" } - diff --git a/tests/cases/standalone/select/dummy.result b/tests/cases/standalone/select/dummy.result new file mode 100644 index 0000000000..2bae33353a --- /dev/null +++ b/tests/cases/standalone/select/dummy.result @@ -0,0 +1,36 @@ +select 1; + ++--------------------------+ +| Int64(1), #Field, #Int64 | ++--------------------------+ +| 1 | ++--------------------------+ + +select 2 + 3; + ++----------------------------------------+ +| Int64(2) Plus Int64(3), #Field, #Int64 | ++----------------------------------------+ +| 5 | ++----------------------------------------+ + +select 4 + 0.5; + ++----------------------------------------------+ +| Int64(4) Plus Float64(0.5), #Field, #Float64 | ++----------------------------------------------+ +| 4.5 | ++----------------------------------------------+ + +select "a"; + +Failed to execute, error: Datanode { code: 1003, msg: "Failed to execute query: select \"a\";, source: Failed to execute query: select \"a\";, source: Failed to select from table, source: Error occurred on the data node, code: 3000, msg: Failed to execute sql, source: Cannot plan SQL: SELECT \"a\", source: Error during planning: Invalid identifier '#a' for schema fields:[], metadata:{}" } + +select "A"; + +Failed to execute, error: Datanode { code: 1003, msg: "Failed to execute query: select \"A\";, source: Failed to execute query: select \"A\";, source: Failed to select from table, source: Error occurred on the data node, code: 3000, msg: Failed to execute sql, source: Cannot plan SQL: SELECT \"A\", source: Error during planning: Invalid identifier '#A' for schema fields:[], metadata:{}" } + +select * where "a" = "A"; + +Failed to execute, error: Datanode { code: 1003, msg: "Failed to execute query: select * where \"a\" = \"A\";, source: Failed to execute query: select * where \"a\" = \"A\";, source: Failed to select from table, source: Error occurred on the data node, code: 3000, msg: Failed to execute sql, source: Cannot plan SQL: SELECT * WHERE \"a\" = \"A\", source: Error during planning: Invalid identifier '#a' for schema fields:[], metadata:{}" } + diff --git a/tests/runner/src/env.rs b/tests/runner/src/env.rs index c8b571e7fb..1d7a195da5 100644 --- a/tests/runner/src/env.rs +++ b/tests/runner/src/env.rs @@ -51,13 +51,13 @@ impl Env { pub async fn start_standalone() -> GreptimeDB { let server_process = Command::new("cargo") .current_dir("../") - .args(["run", "--", "standalone", "start"]) + .args(["run", "--", "standalone", "start", "-m"]) .spawn() .unwrap_or_else(|_| panic!("Failed to start GreptimeDB")); time::sleep(Duration::from_secs(3)).await; - let client = Client::with_urls(vec!["127.0.0.1:3001"]); + let client = Client::with_urls(vec!["127.0.0.1:4001"]); let db = DB::new("greptime", client.clone()); GreptimeDB {