diff --git a/common/ast/src/ast/statements/statement.rs b/common/ast/src/ast/statements/statement.rs index 26ea84d337dc3..4c075edc2de3b 100644 --- a/common/ast/src/ast/statements/statement.rs +++ b/common/ast/src/ast/statements/statement.rs @@ -52,6 +52,7 @@ pub enum Statement<'a> { }, SetVariable { + is_global: bool, variable: Identifier<'a>, value: Literal, }, @@ -215,7 +216,17 @@ impl<'a> Display for Statement<'a> { } write!(f, " {object_id}")?; } - Statement::SetVariable { variable, value } => write!(f, "SET {variable} = {value}")?, + Statement::SetVariable { + is_global, + variable, + value, + } => { + write!(f, "SET ")?; + if *is_global { + write!(f, "GLOBAL ")?; + } + write!(f, "{variable} = {value}")?; + } Statement::ShowDatabases(stmt) => write!(f, "{stmt}")?, Statement::ShowCreateDatabase(stmt) => write!(f, "{stmt}")?, Statement::CreateDatabase(stmt) => write!(f, "{stmt}")?, diff --git a/common/ast/src/parser/statement.rs b/common/ast/src/parser/statement.rs index e486c4ff1178b..00eaf7c5f48aa 100644 --- a/common/ast/src/parser/statement.rs +++ b/common/ast/src/parser/statement.rs @@ -110,9 +110,13 @@ pub fn statement(i: Input) -> IResult { ); let set_variable = map( rule! { - SET ~ #ident ~ "=" ~ #literal + SET ~ (GLOBAL)? ~ #ident ~ "=" ~ #literal + }, + |(_, opt_is_global, variable, _, value)| Statement::SetVariable { + is_global: opt_is_global.is_some(), + variable, + value, }, - |(_, variable, _, value)| Statement::SetVariable { variable, value }, ); let show_databases = map( rule! { diff --git a/common/ast/src/parser/token.rs b/common/ast/src/parser/token.rs index e833fb18bcd45..2fd1677364407 100644 --- a/common/ast/src/parser/token.rs +++ b/common/ast/src/parser/token.rs @@ -401,6 +401,8 @@ pub enum TokenKind { FUSE, #[token("GITHUB", ignore(ascii_case))] GITHUB, + #[token("GLOBAL", ignore(ascii_case))] + GLOBAL, #[token("GRAPH", ignore(ascii_case))] GRAPH, #[token("GROUP", ignore(ascii_case))] diff --git a/common/management/src/lib.rs b/common/management/src/lib.rs index d754b4aa4b7ed..45cecdb302d94 100644 --- a/common/management/src/lib.rs +++ b/common/management/src/lib.rs @@ -16,6 +16,7 @@ mod cluster; mod quota; mod role; mod serde; +mod setting; mod stage; mod udf; mod user; @@ -28,6 +29,8 @@ pub use role::RoleApi; pub use role::RoleMgr; pub use serde::deserialize_struct; pub use serde::serialize_struct; +pub use setting::SettingApi; +pub use setting::SettingMgr; pub use stage::StageApi; pub use stage::StageMgr; pub use udf::UdfApi; diff --git a/common/management/src/setting/mod.rs b/common/management/src/setting/mod.rs new file mode 100644 index 0000000000000..5f6e3965d25ba --- /dev/null +++ b/common/management/src/setting/mod.rs @@ -0,0 +1,19 @@ +// 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. + +mod setting_api; +mod setting_mgr; + +pub use setting_api::SettingApi; +pub use setting_mgr::SettingMgr; diff --git a/common/management/src/setting/setting_api.rs b/common/management/src/setting/setting_api.rs new file mode 100644 index 0000000000000..85accf35c62aa --- /dev/null +++ b/common/management/src/setting/setting_api.rs @@ -0,0 +1,31 @@ +// 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 common_exception::Result; +use common_meta_types::SeqV; +use common_meta_types::UserSetting; + +#[async_trait::async_trait] +pub trait SettingApi: Sync + Send { + // Add a setting to /tenant/cluster/setting-name. + async fn set_setting(&self, setting: UserSetting) -> Result; + + // Get all the settings for tenant/cluster. + async fn get_settings(&self) -> Result>; + + async fn get_setting(&self, name: &str, seq: Option) -> Result>; + + // Drop the setting by name. + async fn drop_setting(&self, name: &str, seq: Option) -> Result<()>; +} diff --git a/common/management/src/setting/setting_mgr.rs b/common/management/src/setting/setting_mgr.rs new file mode 100644 index 0000000000000..10db14c708f01 --- /dev/null +++ b/common/management/src/setting/setting_mgr.rs @@ -0,0 +1,113 @@ +// 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_exception::ErrorCode; +use common_exception::Result; +use common_meta_api::KVApi; +use common_meta_types::IntoSeqV; +use common_meta_types::MatchSeq; +use common_meta_types::MatchSeqExt; +use common_meta_types::OkOrExist; +use common_meta_types::Operation; +use common_meta_types::SeqV; +use common_meta_types::UpsertKVReq; +use common_meta_types::UserSetting; + +use crate::setting::SettingApi; + +static USER_SETTING_API_KEY_PREFIX: &str = "__fd_settings"; + +pub struct SettingMgr { + kv_api: Arc, + setting_prefix: String, +} + +impl SettingMgr { + #[allow(dead_code)] + pub fn create(kv_api: Arc, tenant: &str) -> Result { + Ok(SettingMgr { + kv_api, + setting_prefix: format!("{}/{}", USER_SETTING_API_KEY_PREFIX, tenant), + }) + } +} + +#[async_trait::async_trait] +impl SettingApi for SettingMgr { + async fn set_setting(&self, setting: UserSetting) -> Result { + // Upsert. + let seq = MatchSeq::Any; + let val = Operation::Update(serde_json::to_vec(&setting)?); + let key = format!("{}/{}", self.setting_prefix, setting.name); + let upsert = self + .kv_api + .upsert_kv(UpsertKVReq::new(&key, seq, val, None)); + + let res = upsert.await?.into_add_result()?; + + match res.res { + OkOrExist::Ok(v) => Ok(v.seq), + OkOrExist::Exists(v) => Ok(v.seq), + } + } + + async fn get_settings(&self) -> Result> { + let values = self.kv_api.prefix_list_kv(&self.setting_prefix).await?; + + let mut settings = Vec::with_capacity(values.len()); + for (_, value) in values { + let setting = serde_json::from_slice::(&value.data)?; + settings.push(setting); + } + Ok(settings) + } + + async fn get_setting(&self, name: &str, seq: Option) -> Result> { + let key = format!("{}/{}", self.setting_prefix, name); + let kv_api = self.kv_api.clone(); + let get_kv = async move { kv_api.get_kv(&key).await }; + let res = get_kv.await?; + let seq_value = + res.ok_or_else(|| ErrorCode::UnknownVariable(format!("Unknown setting {}", name)))?; + + match MatchSeq::from(seq).match_seq(&seq_value) { + Ok(_) => Ok(seq_value.into_seqv()?), + Err(_) => Err(ErrorCode::UnknownVariable(format!( + "Unknown setting {}", + name + ))), + } + } + + async fn drop_setting(&self, name: &str, seq: Option) -> Result<()> { + let key = format!("{}/{}", self.setting_prefix, name); + let kv_api = self.kv_api.clone(); + let upsert_kv = async move { + kv_api + .upsert_kv(UpsertKVReq::new(&key, seq.into(), Operation::Delete, None)) + .await + }; + let res = upsert_kv.await?; + if res.prev.is_some() && res.result.is_none() { + Ok(()) + } else { + Err(ErrorCode::UnknownVariable(format!( + "Unknown setting {}", + name + ))) + } + } +} diff --git a/common/management/tests/it/main.rs b/common/management/tests/it/main.rs index 58b575b0419c6..32544f805b8c8 100644 --- a/common/management/tests/it/main.rs +++ b/common/management/tests/it/main.rs @@ -13,6 +13,7 @@ // limitations under the License. mod cluster; +mod setting; mod stage; mod udf; mod user; diff --git a/common/management/tests/it/setting.rs b/common/management/tests/it/setting.rs new file mode 100644 index 0000000000000..86d5ca00af111 --- /dev/null +++ b/common/management/tests/it/setting.rs @@ -0,0 +1,113 @@ +// 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_base::base::tokio; +use common_datavalues::DataValue; +use common_exception::Result; +use common_management::*; +use common_meta_api::KVApi; +use common_meta_embedded::MetaEmbedded; +use common_meta_types::SeqV; +use common_meta_types::UserSetting; + +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +async fn test_set_setting() -> Result<()> { + let (kv_api, mgr) = new_setting_api().await?; + + { + let setting = UserSetting::create("max_threads", DataValue::UInt64(3)); + mgr.set_setting(setting.clone()).await?; + let value = kv_api + .get_kv("__fd_settings/databend_query/max_threads") + .await?; + + match value { + Some(SeqV { + seq: 1, + meta: _, + data: value, + }) => { + assert_eq!(value, serde_json::to_vec(&setting)?); + } + catch => panic!("GetKVActionReply{:?}", catch), + } + } + + // Set again. + { + let setting = UserSetting::create("max_threads", DataValue::UInt64(1)); + mgr.set_setting(setting.clone()).await?; + let value = kv_api + .get_kv("__fd_settings/databend_query/max_threads") + .await?; + + match value { + Some(SeqV { + seq: 2, + meta: _, + data: value, + }) => { + assert_eq!(value, serde_json::to_vec(&setting)?); + } + catch => panic!("GetKVActionReply{:?}", catch), + } + } + + // Get settings. + { + let expect = vec![UserSetting::create("max_threads", DataValue::UInt64(1))]; + let actual = mgr.get_settings().await?; + assert_eq!(actual, expect); + } + + // Get setting. + { + let expect = UserSetting::create("max_threads", DataValue::UInt64(1)); + let actual = mgr.get_setting("max_threads", None).await?; + assert_eq!(actual.data, expect); + } + + // Drop setting. + { + mgr.drop_setting("max_threads", None).await?; + } + + // Get settings. + { + let actual = mgr.get_settings().await?; + assert_eq!(0, actual.len()); + } + + // Get setting. + { + let res = mgr.get_setting("max_threads", None).await; + assert!(res.is_err()); + } + + // Drop setting not exists. + { + let res = mgr.drop_setting("max_threads_not", None).await; + assert!(res.is_err()); + } + + Ok(()) +} + +async fn new_setting_api() -> Result<(Arc, SettingMgr)> { + let test_api = Arc::new(MetaEmbedded::new_temp().await?); + let mgr = SettingMgr::create(test_api.clone(), "databend_query")?; + Ok((test_api, mgr)) +} diff --git a/common/meta/types/src/lib.rs b/common/meta/types/src/lib.rs index 5b1e0a5cea188..3511bb09446e3 100644 --- a/common/meta/types/src/lib.rs +++ b/common/meta/types/src/lib.rs @@ -47,6 +47,7 @@ mod user_identity; mod user_info; mod user_privilege; mod user_quota; +mod user_setting; mod user_stage; pub mod app_error; @@ -179,4 +180,5 @@ pub use user_info::UserOptionFlag; pub use user_privilege::UserPrivilegeSet; pub use user_privilege::UserPrivilegeType; pub use user_quota::UserQuota; +pub use user_setting::UserSetting; pub use user_stage::*; diff --git a/common/meta/types/src/user_setting.rs b/common/meta/types/src/user_setting.rs new file mode 100644 index 0000000000000..93fc447f12b80 --- /dev/null +++ b/common/meta/types/src/user_setting.rs @@ -0,0 +1,58 @@ +// 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 common_datavalues::DataValue; +use common_exception::ErrorCode; +use common_exception::Result; +use serde::Deserialize; +use serde::Serialize; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +#[serde(default)] +pub struct UserSetting { + // The name of the setting. + pub name: String, + // The value of the setting. + pub value: DataValue, +} +impl UserSetting { + pub fn create(name: &str, value: DataValue) -> UserSetting { + UserSetting { + name: name.to_string(), + value, + } + } +} +impl Default for UserSetting { + fn default() -> Self { + UserSetting { + name: "".to_string(), + value: DataValue::Null, + } + } +} + +impl TryFrom> for UserSetting { + type Error = ErrorCode; + + fn try_from(value: Vec) -> Result { + match serde_json::from_slice(&value) { + Ok(info) => Ok(info), + Err(serialize_error) => Err(ErrorCode::IllegalUserInfoFormat(format!( + "Cannot deserialize setting from bytes. cause {}", + serialize_error + ))), + } + } +} diff --git a/common/planners/src/plan_setting.rs b/common/planners/src/plan_setting.rs index 16ae0a65fa749..307a58f5cdfa0 100644 --- a/common/planners/src/plan_setting.rs +++ b/common/planners/src/plan_setting.rs @@ -19,6 +19,7 @@ use common_datavalues::DataSchemaRef; #[derive(serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] pub struct VarValue { + pub is_global: bool, pub variable: String, pub value: String, } diff --git a/common/users/src/lib.rs b/common/users/src/lib.rs index 8463c5f561631..d6a336d954f17 100644 --- a/common/users/src/lib.rs +++ b/common/users/src/lib.rs @@ -17,6 +17,7 @@ mod role_mgr; mod user; mod user_api; mod user_mgr; +mod user_setting; mod user_stage; mod user_udf; diff --git a/common/users/src/user_api.rs b/common/users/src/user_api.rs index 87fd69fefecc1..927eb1fbb6b15 100644 --- a/common/users/src/user_api.rs +++ b/common/users/src/user_api.rs @@ -20,6 +20,8 @@ use common_management::QuotaApi; use common_management::QuotaMgr; use common_management::RoleApi; use common_management::RoleMgr; +use common_management::SettingApi; +use common_management::SettingMgr; use common_management::StageApi; use common_management::StageMgr; use common_management::UdfApi; @@ -60,4 +62,8 @@ impl UserApiProvider { pub fn get_tenant_quota_api_client(&self, tenant: &str) -> Result> { Ok(Arc::new(QuotaMgr::create(self.client.clone(), tenant)?)) } + + pub fn get_setting_api_client(&self, tenant: &str) -> Result> { + Ok(Arc::new(SettingMgr::create(self.client.clone(), tenant)?)) + } } diff --git a/common/users/src/user_setting.rs b/common/users/src/user_setting.rs new file mode 100644 index 0000000000000..d5a09fd8464ef --- /dev/null +++ b/common/users/src/user_setting.rs @@ -0,0 +1,38 @@ +// 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 common_exception::Result; +use common_meta_types::UserSetting; + +use crate::UserApiProvider; + +impl UserApiProvider { + // Set a setting. + pub async fn set_setting(&self, tenant: &str, setting: UserSetting) -> Result { + let setting_api_provider = self.get_setting_api_client(tenant)?; + setting_api_provider.set_setting(setting).await + } + + // Get all settings list. + pub async fn get_settings(&self, tenant: &str) -> Result> { + let setting_api_provider = self.get_setting_api_client(tenant)?; + setting_api_provider.get_settings().await + } + + // Drop a setting by name. + pub async fn drop_setting(&self, tenant: &str, name: &str) -> Result<()> { + let setting_api_provider = self.get_setting_api_client(tenant)?; + setting_api_provider.drop_setting(name, None).await + } +} diff --git a/query/src/interpreters/interpreter_factory_v2.rs b/query/src/interpreters/interpreter_factory_v2.rs index 08365cd62db5c..af1bd36cacffc 100644 --- a/query/src/interpreters/interpreter_factory_v2.rs +++ b/query/src/interpreters/interpreter_factory_v2.rs @@ -86,6 +86,7 @@ impl InterpreterFactoryV2 { | DfStatement::RevokeRole(_) | DfStatement::RevokePrivilege(_) | DfStatement::Call(_) + | DfStatement::SetVariable(_) ) } @@ -299,6 +300,11 @@ impl InterpreterFactoryV2 { ctx, *presign.clone(), )?)), + + Plan::SetVariable(set_variable) => Ok(Arc::new(SettingInterpreter::try_create( + ctx, + *set_variable.clone(), + )?)), } } } diff --git a/query/src/interpreters/interpreter_setting.rs b/query/src/interpreters/interpreter_setting.rs index 613decc10bf6a..411b384112351 100644 --- a/query/src/interpreters/interpreter_setting.rs +++ b/query/src/interpreters/interpreter_setting.rs @@ -57,14 +57,16 @@ impl Interpreter for SettingInterpreter { let _ = tz.parse::().map_err(|_| { ErrorCode::InvalidTimezone(format!("Invalid Timezone: {}", var.value)) })?; - self.ctx - .get_settings() - .set_settings(var.variable, tz.to_string(), false)?; + self.ctx.get_settings().set_settings( + var.variable, + tz.to_string(), + var.is_global, + )?; } _ => { self.ctx .get_settings() - .set_settings(var.variable, var.value, false)?; + .set_settings(var.variable, var.value, var.is_global)?; } } } diff --git a/query/src/sessions/session.rs b/query/src/sessions/session.rs index 87f2cf47036e7..ff8c97ccfa849 100644 --- a/query/src/sessions/session.rs +++ b/query/src/sessions/session.rs @@ -63,7 +63,8 @@ impl Session { mysql_connection_id: Option, ) -> Result> { let session_ctx = Arc::new(SessionContext::try_create(conf.clone())?); - let session_settings = Settings::try_create(&conf)?; + let user_api = session_mgr.get_user_api_provider(); + let session_settings = Settings::try_create(&conf, user_api, session_ctx.clone())?; let ref_count = Arc::new(AtomicUsize::new(0)); let status = Arc::new(Default::default()); Ok(Arc::new(Session { diff --git a/query/src/sessions/session_settings.rs b/query/src/sessions/session_settings.rs index e36b775454352..fb8668c66e4f7 100644 --- a/query/src/sessions/session_settings.rs +++ b/query/src/sessions/session_settings.rs @@ -16,14 +16,18 @@ use std::collections::BTreeMap; use std::collections::HashMap; use std::fmt::Debug; use std::fmt::Formatter; +use std::str; use std::sync::Arc; use common_datavalues::prelude::*; use common_exception::ErrorCode; use common_exception::Result; +use common_meta_types::UserSetting; +use common_users::UserApiProvider; use itertools::Itertools; use parking_lot::RwLock; +use super::SessionContext; use crate::Config; #[derive(Clone)] @@ -58,10 +62,18 @@ pub struct SettingValue { #[derive(Clone)] pub struct Settings { settings: Arc>>, + #[allow(dead_code)] + user_api: Arc, + #[allow(dead_code)] + session_ctx: Arc, } impl Settings { - pub fn try_create(conf: &Config) -> Result { + pub fn try_create( + conf: &Config, + user_api: Arc, + session_ctx: Arc, + ) -> Result { let values = vec![ // max_block_size SettingValue { @@ -182,7 +194,24 @@ impl Settings { } } - let ret = Settings { settings }; + let ret = Settings { + settings, + user_api, + session_ctx, + }; + + // Overwrite settings from metasrv + { + let tenant = &ret.session_ctx.get_current_tenant(); + let global_settings = futures::executor::block_on( + ret.user_api.get_setting_api_client(tenant)?.get_settings(), + )?; + for global_setting in global_settings { + let name = global_setting.name; + let val = String::from_utf8(global_setting.value.as_string()?).unwrap(); + ret.set_settings(name, val, true)?; + } + } // Overwrite settings from conf. { @@ -334,23 +363,43 @@ impl Settings { } // Set u64 value to settings map, if is_global will write to metasrv. - fn try_set_u64(&self, key: &str, val: u64, _is_global: bool) -> Result<()> { + fn try_set_u64(&self, key: &str, val: u64, is_global: bool) -> Result<()> { let mut settings = self.settings.write(); let mut setting = settings .get_mut(key) .ok_or_else(|| ErrorCode::UnknownVariable(format!("Unknown variable: {:?}", key)))?; setting.user_setting.value = DataValue::UInt64(val); + if is_global { + let tenant = self.session_ctx.get_current_tenant(); + let _ = futures::executor::block_on( + self.user_api + .get_setting_api_client(&tenant)? + .set_setting(setting.user_setting.clone()), + )?; + setting.level = ScopeLevel::Global; + } + Ok(()) } - fn try_set_string(&self, key: &str, val: Vec, _is_global: bool) -> Result<()> { + fn try_set_string(&self, key: &str, val: Vec, is_global: bool) -> Result<()> { let mut settings = self.settings.write(); let mut setting = settings .get_mut(key) .ok_or_else(|| ErrorCode::UnknownVariable(format!("Unknown variable: {:?}", key)))?; setting.user_setting.value = DataValue::String(val); + if is_global { + let tenant = self.session_ctx.get_current_tenant(); + let _ = futures::executor::block_on( + self.user_api + .get_setting_api_client(&tenant)? + .set_setting(setting.user_setting.clone()), + )?; + setting.level = ScopeLevel::Global; + } + Ok(()) } @@ -394,6 +443,8 @@ impl Settings { } Settings { settings: new_settings, + user_api: self.user_api.clone(), + session_ctx: self.session_ctx.clone(), } } @@ -459,21 +510,3 @@ impl Settings { Ok(()) } } - -#[derive(Clone, Debug)] -pub struct UserSetting { - // The name of the setting. - pub name: String, - - // The value of the setting. - pub value: DataValue, -} - -impl UserSetting { - pub fn create(name: &str, value: DataValue) -> UserSetting { - UserSetting { - name: name.to_string(), - value, - } - } -} diff --git a/query/src/sql/planner/binder/mod.rs b/query/src/sql/planner/binder/mod.rs index e07a05be0bff4..19796d0a9956b 100644 --- a/query/src/sql/planner/binder/mod.rs +++ b/query/src/sql/planner/binder/mod.rs @@ -55,6 +55,7 @@ mod scalar; mod scalar_common; mod scalar_visitor; mod select; +mod setting; mod show; mod sort; mod table; @@ -257,6 +258,15 @@ impl<'a> Binder { Statement::Presign(stmt) => self.bind_presign(bind_context, stmt).await?, + Statement::SetVariable { + is_global, + variable, + value, + } => { + self.bind_set_variable(bind_context, *is_global, variable, value) + .await? + } + _ => { return Err(ErrorCode::UnImplement(format!( "UnImplemented stmt {stmt} in binder" diff --git a/query/src/sql/planner/binder/setting.rs b/query/src/sql/planner/binder/setting.rs new file mode 100644 index 0000000000000..50706bc9291b4 --- /dev/null +++ b/query/src/sql/planner/binder/setting.rs @@ -0,0 +1,48 @@ +// 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 common_ast::ast::Identifier; +use common_ast::ast::Literal; +use common_exception::Result; +use common_planners::SettingPlan; +use common_planners::VarValue; + +use super::BindContext; +use super::Binder; +use crate::sql::planner::semantic::TypeChecker; +use crate::sql::plans::Plan; + +impl<'a> Binder { + pub(in crate::sql::planner::binder) async fn bind_set_variable( + &mut self, + bind_context: &BindContext, + is_global: bool, + variable: &Identifier<'a>, + value: &Literal, + ) -> Result { + let type_checker = TypeChecker::new(bind_context, self.ctx.clone(), self.metadata.clone()); + + let variable = variable.name.clone(); + + let box (value, _data_type) = type_checker.resolve_literal(value, None)?; + let value = String::from_utf8(value.as_string()?)?; + + let vars = vec![VarValue { + is_global, + variable, + value, + }]; + Ok(Plan::SetVariable(Box::new(SettingPlan { vars }))) + } +} diff --git a/query/src/sql/planner/format/display_plan.rs b/query/src/sql/planner/format/display_plan.rs index ba7d64ecfb7f9..7acd052be3200 100644 --- a/query/src/sql/planner/format/display_plan.rs +++ b/query/src/sql/planner/format/display_plan.rs @@ -99,6 +99,8 @@ impl Plan { Plan::DropRole(drop_role) => Ok(format!("{:?}", drop_role)), Plan::Presign(presign) => Ok(format!("{:?}", presign)), + + Plan::SetVariable(_set_variable) => Ok("SET".to_string()), } } } diff --git a/query/src/sql/planner/plans/mod.rs b/query/src/sql/planner/plans/mod.rs index 4100dfea55e08..d9fd30a01b105 100644 --- a/query/src/sql/planner/plans/mod.rs +++ b/query/src/sql/planner/plans/mod.rs @@ -76,6 +76,7 @@ use common_planners::RenameDatabasePlan; use common_planners::RenameTablePlan; use common_planners::RevokePrivilegePlan; use common_planners::RevokeRolePlan; +use common_planners::SettingPlan; use common_planners::ShowCreateDatabasePlan; use common_planners::ShowCreateTablePlan; use common_planners::ShowDatabasesPlan; @@ -198,6 +199,9 @@ pub enum Plan { // Presign Presign(Box), + + // Set + SetVariable(Box), } impl Display for Plan { @@ -255,6 +259,7 @@ impl Display for Plan { Plan::Delete(_) => write!(f, "Delete"), Plan::Call(_) => write!(f, "Call"), Plan::Presign(_) => write!(f, "Presign"), + Plan::SetVariable(_) => write!(f, "SetVariable"), } } } @@ -320,6 +325,7 @@ impl Plan { Plan::Delete(_) => Arc::new(DataSchema::empty()), Plan::Call(_) => Arc::new(DataSchema::empty()), Plan::Presign(plan) => plan.schema(), + Plan::SetVariable(plan) => plan.schema(), } } } diff --git a/query/src/sql/statements/statement_set_variable.rs b/query/src/sql/statements/statement_set_variable.rs index e15efc487f084..3640d02854812 100644 --- a/query/src/sql/statements/statement_set_variable.rs +++ b/query/src/sql/statements/statement_set_variable.rs @@ -56,6 +56,7 @@ impl AnalyzableStatement for DfSetVariable { impl DfSetVariable { fn mapping_set_var(variable: String, value: &SetVariableValue) -> VarValue { VarValue { + is_global: false, variable, value: match value { sqlparser::ast::SetVariableValue::Ident(v) => v.value.clone(), diff --git a/tests/suites/0_stateless/06_show/06_0003_show_settings_v2.result b/tests/suites/0_stateless/06_show/06_0003_show_settings_v2.result index 97dca5fbbe620..377477cf2042a 100644 --- a/tests/suites/0_stateless/06_show/06_0003_show_settings_v2.result +++ b/tests/suites/0_stateless/06_show/06_0003_show_settings_v2.result @@ -17,3 +17,4 @@ wait_for_async_insert_timeout 100 100 SESSION The timeout in seconds for waiting enable_async_insert 0 0 SESSION Whether the client open async insert mode, default value: 0 UInt64 enable_new_processor_framework 1 1 SESSION Enable new processor framework if value != 0, default value: 1 UInt64 enable_planner_v2 1 0 SESSION Enable planner v2 by setting this variable to 1, default value: 0 UInt64 +max_threads 12 16 GLOBAL The maximum number of threads to execute the request. By default, it is determined automatically. UInt64 diff --git a/tests/suites/0_stateless/06_show/06_0003_show_settings_v2.sql b/tests/suites/0_stateless/06_show/06_0003_show_settings_v2.sql index a58f6ec0ba6ec..00af19fa3bcd5 100644 --- a/tests/suites/0_stateless/06_show/06_0003_show_settings_v2.sql +++ b/tests/suites/0_stateless/06_show/06_0003_show_settings_v2.sql @@ -3,4 +3,8 @@ SET max_threads=11; SET unknown_settings=11; -- {ErrorCode 2801} SHOW SETTINGS; SHOW SETTINGS LIKE 'enable%'; + +SET GLOBAL max_threads=12; +SHOW SETTINGS LIKE 'max_threads'; + SET enable_planner_v2=0;