From 90ba0ddd9026ad30cf70de1f9363da730b8d06ca Mon Sep 17 00:00:00 2001 From: cadl Date: Sun, 17 Apr 2022 12:27:39 +0800 Subject: [PATCH] bug(sql): add EmptyInterpreter to fix /*\!xx*/ empty query --- query/src/interpreters/interpreter_empty.rs | 53 ++++++++++++++++ query/src/interpreters/interpreter_factory.rs | 2 + query/src/interpreters/mod.rs | 2 + query/src/sql/plan_parser.rs | 3 +- .../it/interpreters/interpreter_empty.rs | 60 +++++++++++++++++++ query/tests/it/interpreters/mod.rs | 1 + .../it/servers/http/clickhouse_handler.rs | 12 ++-- .../03_dml/03_0019_select_empty.result | 3 + .../03_dml/03_0019_select_empty.sql | 3 + 9 files changed, 132 insertions(+), 7 deletions(-) create mode 100644 query/src/interpreters/interpreter_empty.rs create mode 100644 query/tests/it/interpreters/interpreter_empty.rs create mode 100644 tests/suites/0_stateless/03_dml/03_0019_select_empty.result create mode 100644 tests/suites/0_stateless/03_dml/03_0019_select_empty.sql diff --git a/query/src/interpreters/interpreter_empty.rs b/query/src/interpreters/interpreter_empty.rs new file mode 100644 index 000000000000..09c4fe2e687d --- /dev/null +++ b/query/src/interpreters/interpreter_empty.rs @@ -0,0 +1,53 @@ +// Copyright 2022 Datafuse Labs. +// +// 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 std::sync::Arc; + +use common_datavalues::prelude::*; +use common_exception::Result; +use common_planners::EmptyPlan; +use common_streams::DataBlockStream; +use common_streams::SendableDataBlockStream; + +use crate::interpreters::Interpreter; +use crate::interpreters::InterpreterPtr; +use crate::sessions::QueryContext; + +// EmptyInterpreter is a Empty interpreter to execute nothing. +// Such as the query '/*!40101*/', it makes the front-end (e.g. MySQL handler) no need check the EmptyPlan or not. +pub struct EmptyInterpreter {} + +impl EmptyInterpreter { + pub fn try_create(_ctx: Arc, _plan: EmptyPlan) -> Result { + Ok(Arc::new(EmptyInterpreter {})) + } +} + +#[async_trait::async_trait] +impl Interpreter for EmptyInterpreter { + fn name(&self) -> &str { + "EmptyInterpreter" + } + + async fn execute( + &self, + _input_stream: Option, + ) -> Result { + Ok(Box::pin(DataBlockStream::create( + Arc::new(DataSchema::empty()), + None, + vec![], + ))) + } +} diff --git a/query/src/interpreters/interpreter_factory.rs b/query/src/interpreters/interpreter_factory.rs index 42a1922301fe..9de7dab24b4b 100644 --- a/query/src/interpreters/interpreter_factory.rs +++ b/query/src/interpreters/interpreter_factory.rs @@ -43,6 +43,7 @@ use crate::interpreters::DropTableInterpreter; use crate::interpreters::DropUserInterpreter; use crate::interpreters::DropUserUDFInterpreter; use crate::interpreters::DropViewInterpreter; +use crate::interpreters::EmptyInterpreter; use crate::interpreters::ExplainInterpreter; use crate::interpreters::GrantPrivilegeInterpreter; use crate::interpreters::GrantRoleInterpreter; @@ -172,6 +173,7 @@ impl InterpreterFactory { PlanNode::UseDatabase(v) => UseDatabaseInterpreter::try_create(ctx_clone, v), PlanNode::Kill(v) => KillInterpreter::try_create(ctx_clone, v), PlanNode::SetVariable(v) => SettingInterpreter::try_create(ctx_clone, v), + PlanNode::Empty(v) => EmptyInterpreter::try_create(ctx_clone, v), _ => Result::Err(ErrorCode::UnknownTypeOfQuery(format!( "Can't get the interpreter by plan:{}", diff --git a/query/src/interpreters/mod.rs b/query/src/interpreters/mod.rs index d32513878485..e6565ce05455 100644 --- a/query/src/interpreters/mod.rs +++ b/query/src/interpreters/mod.rs @@ -19,6 +19,7 @@ mod interpreter_copy; mod interpreter_database_create; mod interpreter_database_drop; mod interpreter_database_show_create; +mod interpreter_empty; mod interpreter_explain; mod interpreter_factory; mod interpreter_factory_interceptor; @@ -76,6 +77,7 @@ pub use interpreter_copy::CopyInterpreter; pub use interpreter_database_create::CreateDatabaseInterpreter; pub use interpreter_database_drop::DropDatabaseInterpreter; pub use interpreter_database_show_create::ShowCreateDatabaseInterpreter; +pub use interpreter_empty::EmptyInterpreter; pub use interpreter_explain::ExplainInterpreter; pub use interpreter_factory::InterpreterFactory; pub use interpreter_factory_interceptor::InterceptorInterpreter; diff --git a/query/src/sql/plan_parser.rs b/query/src/sql/plan_parser.rs index 81ea7119cf67..9017eaafbdf7 100644 --- a/query/src/sql/plan_parser.rs +++ b/query/src/sql/plan_parser.rs @@ -16,6 +16,7 @@ use std::sync::Arc; use common_exception::ErrorCode; use common_exception::Result; +use common_planners::EmptyPlan; use common_planners::ExplainPlan; use common_planners::Expression; use common_planners::PlanBuilder; @@ -54,7 +55,7 @@ impl PlanParser { ctx: Arc, ) -> Result { if statements.is_empty() { - return Err(ErrorCode::SyntaxException("Empty query")); + return Ok(PlanNode::Empty(EmptyPlan::create())); } else if statements.len() > 1 { return Err(ErrorCode::SyntaxException("Only support single query")); } diff --git a/query/tests/it/interpreters/interpreter_empty.rs b/query/tests/it/interpreters/interpreter_empty.rs new file mode 100644 index 000000000000..dd6dd5b1d07e --- /dev/null +++ b/query/tests/it/interpreters/interpreter_empty.rs @@ -0,0 +1,60 @@ +// Copyright 2021 Datafuse Labs. +// +// 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 common_base::tokio; +use common_exception::Result; +use databend_query::interpreters::*; +use databend_query::sql::*; +use futures::TryStreamExt; +use pretty_assertions::assert_eq; + +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +async fn test_empty_interpreter() -> Result<()> { + common_tracing::init_default_ut_tracing(); + let ctx = crate::tests::create_query_context().await?; + + { + let query = "/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */"; + let plan = PlanParser::parse(ctx.clone(), query).await?; + let executor = InterpreterFactory::get(ctx.clone(), plan)?; + assert_eq!(executor.name(), "EmptyInterpreter"); + + let stream = executor.execute(None).await?; + let result = stream.try_collect::>().await; + assert!(result.is_ok()) + } + + { + let query = "/*!40101*/select number from numbers_mt(1)"; + let plan = PlanParser::parse(ctx.clone(), query).await?; + let executor = InterpreterFactory::get(ctx.clone(), plan)?; + assert_eq!(executor.name(), "SelectInterpreter"); + + let stream = executor.execute(None).await?; + let result = stream.try_collect::>().await?; + let block = &result[0]; + assert_eq!(block.num_columns(), 1); + + let expected = vec![ + "+--------+", + "| number |", + "+--------+", + "| 0 |", + "+--------+", + ]; + common_datablocks::assert_blocks_sorted_eq(expected, result.as_slice()); + } + + Ok(()) +} diff --git a/query/tests/it/interpreters/mod.rs b/query/tests/it/interpreters/mod.rs index 58ad77b8f712..515b4ea7e300 100644 --- a/query/tests/it/interpreters/mod.rs +++ b/query/tests/it/interpreters/mod.rs @@ -16,6 +16,7 @@ mod interpreter_call; mod interpreter_database_create; mod interpreter_database_drop; mod interpreter_database_show_create; +mod interpreter_empty; mod interpreter_explain; mod interpreter_factory_interceptor; mod interpreter_insert; diff --git a/query/tests/it/servers/http/clickhouse_handler.rs b/query/tests/it/servers/http/clickhouse_handler.rs index ccbee1689259..2a15986b646a 100644 --- a/query/tests/it/servers/http/clickhouse_handler.rs +++ b/query/tests/it/servers/http/clickhouse_handler.rs @@ -45,12 +45,6 @@ macro_rules! assert_ok { async fn test_select() -> PoemResult<()> { let server = Server::new(); - { - let (status, body) = server.get("").await; - assert_eq!(status, StatusCode::BAD_REQUEST); - assert_error!(body, "Empty query"); - } - { let (status, body) = server.get("bad sql").await; assert_eq!(status, StatusCode::BAD_REQUEST); @@ -69,6 +63,12 @@ async fn test_select() -> PoemResult<()> { assert_error!(body, "sql parser error"); } + { + let (status, body) = server.get("").await; + assert_eq!(status, StatusCode::OK); + assert_eq!(&body, ""); + } + { let (status, body) = server.get("select 1").await; assert_eq!(status, StatusCode::OK); diff --git a/tests/suites/0_stateless/03_dml/03_0019_select_empty.result b/tests/suites/0_stateless/03_dml/03_0019_select_empty.result new file mode 100644 index 000000000000..7938dcdde861 --- /dev/null +++ b/tests/suites/0_stateless/03_dml/03_0019_select_empty.result @@ -0,0 +1,3 @@ +0 +1 +0 diff --git a/tests/suites/0_stateless/03_dml/03_0019_select_empty.sql b/tests/suites/0_stateless/03_dml/03_0019_select_empty.sql new file mode 100644 index 000000000000..5919991fc9ae --- /dev/null +++ b/tests/suites/0_stateless/03_dml/03_0019_select_empty.sql @@ -0,0 +1,3 @@ +/*!40101*/select number from numbers_mt(2); +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101*/select number from numbers_mt(1);