diff --git a/src/meta/api/src/share_api_impl.rs b/src/meta/api/src/share_api_impl.rs index d8d1fd5407c21..1ab3903ad07a7 100644 --- a/src/meta/api/src/share_api_impl.rs +++ b/src/meta/api/src/share_api_impl.rs @@ -68,10 +68,10 @@ impl ShareApi for KV { debug!(req = debug(&req), "ShareApi: {}", func_name!()); // Get all outbound share accounts. - let outbound_accounts = get_outbound_shared_accounts_by_tenant(self, &req.tenant).await?; + let outbound_accounts = get_outbound_share_infos_by_tenant(self, &req.tenant).await?; // Get all inbound share accounts. - let inbound_accounts = get_inbound_shared_accounts_by_tenant(self, &req.tenant).await?; + let inbound_accounts = get_inbound_share_infos_by_tenant(self, &req.tenant).await?; Ok(ShowSharesReply { outbound_accounts, @@ -759,11 +759,9 @@ impl ShareApi for KV { &self, req: GetShareGrantTenantsReq, ) -> MetaResult { - let reply = get_outbound_shared_accounts_by_name(self, &req.share_name).await?; + let accounts = get_outbound_share_tenants_by_name(self, &req.share_name).await?; - Ok(GetShareGrantTenantsReply { - accounts: reply.accounts.unwrap_or_default(), - }) + Ok(GetShareGrantTenantsReply { accounts }) } // Return all the grant privileges of the object @@ -920,7 +918,37 @@ async fn get_share_database_name( } } -async fn get_outbound_shared_accounts_by_name( +async fn get_outbound_share_tenants_by_name( + kv_api: &(impl KVApi + ?Sized), + share_name: &ShareNameIdent, +) -> Result, MetaError> { + let res = get_share_or_err(kv_api, share_name, format!("get_share: {share_name}")).await?; + let (_share_id_seq, share_id, _share_meta_seq, share_meta) = res; + + let mut accounts = vec![]; + for account in share_meta.get_accounts() { + let share_account_key = ShareAccountNameIdent { + account: account.clone(), + share_id, + }; + + let (_seq, meta) = get_share_account_meta_or_err( + kv_api, + &share_account_key, + format!("get_outbound_share_tenants_by_name's account: {share_id}/{account}"), + ) + .await?; + + accounts.push(GetShareGrantTenants { + account, + grant_on: meta.share_on, + }); + } + + Ok(accounts) +} + +async fn get_outbound_share_info_by_name( kv_api: &(impl KVApi + ?Sized), share_name: &ShareNameIdent, ) -> Result { @@ -948,7 +976,7 @@ async fn get_outbound_shared_accounts_by_name( }) } -async fn get_outbound_shared_accounts_by_tenant( +async fn get_outbound_share_infos_by_tenant( kv_api: &(impl KVApi + ?Sized), tenant: &str, ) -> Result, MetaError> { @@ -961,7 +989,7 @@ async fn get_outbound_shared_accounts_by_tenant( let share_name_keys = list_keys(kv_api, &tenant_share_name_key).await?; for share_name in share_name_keys { - let reply = get_outbound_shared_accounts_by_name(kv_api, &share_name).await; + let reply = get_outbound_share_info_by_name(kv_api, &share_name).await; if let Ok(reply) = reply { outbound_share_accounts.push(reply) } @@ -970,7 +998,7 @@ async fn get_outbound_shared_accounts_by_tenant( Ok(outbound_share_accounts) } -async fn get_inbound_shared_accounts_by_tenant( +async fn get_inbound_share_infos_by_tenant( kv_api: &(impl KVApi + ?Sized), tenant: &String, ) -> Result, MetaError> { @@ -986,14 +1014,14 @@ async fn get_inbound_shared_accounts_by_tenant( let (_share_meta_seq, share_meta) = get_share_meta_by_id_or_err( kv_api, share_id, - format!("get_inbound_shared_accounts_by_tenant: {}", share_id), + format!("get_inbound_share_infos_by_tenant: {}", share_id), ) .await?; let (_seq, share_name) = get_share_id_to_name_or_err( kv_api, share_id, - format!("get_inbound_shared_accounts_by_tenant: {}", share_id), + format!("get_inbound_share_infos_by_tenant: {}", share_id), ) .await?; let database_name = get_share_database_name(kv_api, &share_meta, &share_name).await?; @@ -1006,7 +1034,7 @@ async fn get_inbound_shared_accounts_by_tenant( kv_api, &share_account_key, format!( - "get_inbound_shared_accounts_by_tenant's account: {}/{}", + "get_inbound_share_infos_by_tenant's account: {}/{}", share_id, tenant ), ) diff --git a/src/meta/api/src/share_api_test_suite.rs b/src/meta/api/src/share_api_test_suite.rs index df1cdda170b44..fc8028fdb0608 100644 --- a/src/meta/api/src/share_api_test_suite.rs +++ b/src/meta/api/src/share_api_test_suite.rs @@ -260,7 +260,7 @@ impl ShareApiTestSuite { assert!(resp.is_ok()); let resp = resp.unwrap(); assert_eq!(resp.accounts.len(), 1); - assert_eq!(resp.accounts[0], account.to_string()); + assert_eq!(resp.accounts[0].account, account.to_string()); } info!("--- share tenant2.share2 to tenant1"); diff --git a/src/meta/app/src/share/mod.rs b/src/meta/app/src/share/mod.rs index 4e10a7bd1564a..ddfcd18cce368 100644 --- a/src/meta/app/src/share/mod.rs +++ b/src/meta/app/src/share/mod.rs @@ -25,6 +25,7 @@ pub use share::GetObjectGrantPrivilegesReply; pub use share::GetObjectGrantPrivilegesReq; pub use share::GetShareGrantObjectReply; pub use share::GetShareGrantObjectReq; +pub use share::GetShareGrantTenants; pub use share::GetShareGrantTenantsReply; pub use share::GetShareGrantTenantsReq; pub use share::GrantShareObjectReply; diff --git a/src/meta/app/src/share/share.rs b/src/meta/app/src/share/share.rs index 398c69e880f7a..e6ff83a581e03 100644 --- a/src/meta/app/src/share/share.rs +++ b/src/meta/app/src/share/share.rs @@ -202,9 +202,15 @@ pub struct GetShareGrantTenantsReq { pub share_name: ShareNameIdent, } +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, PartialEq, Eq)] +pub struct GetShareGrantTenants { + pub account: String, + pub grant_on: DateTime, +} + #[derive(serde::Serialize, serde::Deserialize, Clone, Debug, PartialEq, Eq)] pub struct GetShareGrantTenantsReply { - pub accounts: Vec, + pub accounts: Vec, } #[derive(serde::Serialize, serde::Deserialize, Clone, Debug, PartialEq, Eq)] diff --git a/src/query/ast/src/ast/statements/share.rs b/src/query/ast/src/ast/statements/share.rs index 2a980610a75c0..55525d9e091fe 100644 --- a/src/query/ast/src/ast/statements/share.rs +++ b/src/query/ast/src/ast/statements/share.rs @@ -151,3 +151,29 @@ impl Display for ShowSharesStmt { Ok(()) } } + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ShowObjectGrantPrivilegesStmt { + pub object: ShareGrantObjectName, +} + +impl Display for ShowObjectGrantPrivilegesStmt { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "SHOW GRANTS ON {}", self.object)?; + + Ok(()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ShowGrantsOfShareStmt { + pub share_name: String, +} + +impl Display for ShowGrantsOfShareStmt { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "SHOW GRANTS OF SHARE {}", self.share_name)?; + + Ok(()) + } +} diff --git a/src/query/ast/src/ast/statements/statement.rs b/src/query/ast/src/ast/statements/statement.rs index f78227cb2406a..37c703a2961cc 100644 --- a/src/query/ast/src/ast/statements/statement.rs +++ b/src/query/ast/src/ast/statements/statement.rs @@ -166,6 +166,8 @@ pub enum Statement<'a> { AlterShareTenants(AlterShareTenantsStmt<'a>), DescShare(DescShareStmt<'a>), ShowShares(ShowSharesStmt), + ShowObjectGrantPrivileges(ShowObjectGrantPrivilegesStmt), + ShowGrantsOfShare(ShowGrantsOfShareStmt), } #[derive(Debug, Clone, PartialEq)] @@ -376,6 +378,8 @@ impl<'a> Display for Statement<'a> { Statement::AlterShareTenants(stmt) => write!(f, "{stmt}")?, Statement::DescShare(stmt) => write!(f, "{stmt}")?, Statement::ShowShares(stmt) => write!(f, "{stmt}")?, + Statement::ShowObjectGrantPrivileges(stmt) => write!(f, "{stmt}")?, + Statement::ShowGrantsOfShare(stmt) => write!(f, "{stmt}")?, } Ok(()) } diff --git a/src/query/ast/src/parser/statement.rs b/src/query/ast/src/parser/statement.rs index b03284b0889e6..3795c1320e236 100644 --- a/src/query/ast/src/parser/statement.rs +++ b/src/query/ast/src/parser/statement.rs @@ -37,6 +37,12 @@ use crate::rule; use crate::util::*; use crate::ErrorKind; +pub enum ShowGrantOption { + PrincipalIdentity(PrincipalIdentity), + ShareGrantObjectName(ShareGrantObjectName), + ShareName(String), +} + pub fn statement(i: Input) -> IResult { let explain = map_res( rule! { @@ -544,10 +550,19 @@ pub fn statement(i: Input) -> IResult { ); let show_grants = map( rule! { - SHOW ~ GRANTS ~ (FOR ~ #grant_option)? + SHOW ~ GRANTS ~ #show_grant_option? }, - |(_, _, opt_principal)| Statement::ShowGrants { - principal: opt_principal.map(|(_, principal)| principal), + |(_, _, show_grant_option)| match show_grant_option { + Some(ShowGrantOption::PrincipalIdentity(principal)) => Statement::ShowGrants { + principal: Some(principal), + }, + Some(ShowGrantOption::ShareGrantObjectName(object)) => { + Statement::ShowObjectGrantPrivileges(ShowObjectGrantPrivilegesStmt { object }) + } + Some(ShowGrantOption::ShareName(share_name)) => { + Statement::ShowGrantsOfShare(ShowGrantsOfShareStmt { share_name }) + } + None => Statement::ShowGrants { principal: None }, }, ); let revoke = map( @@ -906,7 +921,7 @@ pub fn statement(i: Input) -> IResult { ), rule!( #grant : "`GRANT { ROLE | schemaObjectPrivileges | ALL [ PRIVILEGES ] ON } TO { [ROLE ] | [USER] }`" - | #show_grants : "`SHOW GRANTS [FOR { ROLE | [USER] }]`" + | #show_grants : "`SHOW GRANTS {FOR { ROLE | USER }] | ON {DATABASE | TABLE .} }`" | #revoke : "`REVOKE { ROLE | schemaObjectPrivileges | ALL [ PRIVILEGES ] ON } FROM { [ROLE ] | [USER] }`" ), rule!( @@ -1146,6 +1161,35 @@ pub fn grant_level(i: Input) -> IResult { )(i) } +pub fn show_grant_option(i: Input) -> IResult { + let grant_role = map( + rule! { + FOR ~ #grant_option + }, + |(_, opt_principal)| ShowGrantOption::PrincipalIdentity(opt_principal), + ); + + let share_object_name = map( + rule! { + ON ~ #grant_share_object_name + }, + |(_, object_name)| ShowGrantOption::ShareGrantObjectName(object_name), + ); + + let share_name = map( + rule! { + OF ~ SHARE ~ #ident + }, + |(_, _, share_name)| ShowGrantOption::ShareName(share_name.to_string()), + ); + + rule!( + #grant_role: "FOR { ROLE | [USER] }" + | #share_object_name: "ON {DATABASE | TABLE .}" + | #share_name: "OF SHARE " + )(i) +} + pub fn grant_option(i: Input) -> IResult { let role = map( rule! { diff --git a/src/query/ast/src/parser/token.rs b/src/query/ast/src/parser/token.rs index 4ceb685a3e803..b64b90eee2eb6 100644 --- a/src/query/ast/src/parser/token.rs +++ b/src/query/ast/src/parser/token.rs @@ -513,6 +513,8 @@ pub enum TokenKind { NULL, #[token("OBJECT", ignore(ascii_case))] OBJECT, + #[token("OF", ignore(ascii_case))] + OF, #[token("OFFSET", ignore(ascii_case))] OFFSET, #[token("ON", ignore(ascii_case))] @@ -917,6 +919,7 @@ impl TokenKind { | TokenKind::LIMIT | TokenKind::OFFSET | TokenKind::ON + | TokenKind::OF | TokenKind::ORDER // | TokenKind::PRECISION // | TokenKind::RETURNING @@ -1027,6 +1030,7 @@ impl TokenKind { // | TokenKind::NOTNULL | TokenKind::OFFSET | TokenKind::ON + | TokenKind::OF | TokenKind::ORDER // | TokenKind::OVERLAPS // | TokenKind::RETURNING diff --git a/src/query/ast/tests/it/parser.rs b/src/query/ast/tests/it/parser.rs index bff2beff6ae7c..b2e24d51b77cc 100644 --- a/src/query/ast/tests/it/parser.rs +++ b/src/query/ast/tests/it/parser.rs @@ -271,6 +271,9 @@ fn test_statement() { r#"DESC SHARE b;"#, r#"DESCRIBE SHARE b;"#, r#"SHOW SHARES;"#, + r#"SHOW GRANTS ON TABLE db1.tb1;"#, + r#"SHOW GRANTS ON DATABASE db;"#, + r#"SHOW GRANTS OF SHARE t;"#, ]; for case in cases { diff --git a/src/query/ast/tests/it/testdata/statement.txt b/src/query/ast/tests/it/testdata/statement.txt index 9ca11b7feb901..665fa7678bb88 100644 --- a/src/query/ast/tests/it/testdata/statement.txt +++ b/src/query/ast/tests/it/testdata/statement.txt @@ -6222,3 +6222,44 @@ ShowShares( ) +---------- Input ---------- +SHOW GRANTS ON TABLE db1.tb1; +---------- Output --------- +SHOW GRANTS ON TABLE db1.tb1 +---------- AST ------------ +ShowObjectGrantPrivileges( + ShowObjectGrantPrivilegesStmt { + object: Table( + "db1", + "tb1", + ), + }, +) + + +---------- Input ---------- +SHOW GRANTS ON DATABASE db; +---------- Output --------- +SHOW GRANTS ON DATABASE db +---------- AST ------------ +ShowObjectGrantPrivileges( + ShowObjectGrantPrivilegesStmt { + object: Database( + "db", + ), + }, +) + + +---------- Input ---------- +SHOW GRANTS OF SHARE t; +---------- Output --------- +SHOW GRANTS OF SHARE t +---------- AST ------------ +ShowGrantsOfShare( + ShowGrantsOfShareStmt { + share_name: "t", + }, +) + + diff --git a/src/query/service/src/interpreters/interpreter_factory_v2.rs b/src/query/service/src/interpreters/interpreter_factory_v2.rs index 4af6e4d14bdef..ef39c12961ec0 100644 --- a/src/query/service/src/interpreters/interpreter_factory_v2.rs +++ b/src/query/service/src/interpreters/interpreter_factory_v2.rs @@ -272,6 +272,12 @@ impl InterpreterFactoryV2 { ctx, *p.clone(), )?)), + Plan::ShowObjectGrantPrivileges(p) => Ok(Arc::new( + ShowObjectGrantPrivilegesInterpreter::try_create(ctx, *p.clone())?, + )), + Plan::ShowGrantTenantsOfShare(p) => Ok(Arc::new( + ShowGrantTenantsOfShareInterpreter::try_create(ctx, *p.clone())?, + )), } } } diff --git a/src/query/service/src/interpreters/interpreter_share_show_grant_tenants.rs b/src/query/service/src/interpreters/interpreter_share_show_grant_tenants.rs new file mode 100644 index 0000000000000..9057ed05303d8 --- /dev/null +++ b/src/query/service/src/interpreters/interpreter_share_show_grant_tenants.rs @@ -0,0 +1,91 @@ +// 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_datablocks::DataBlock; +use common_datavalues::prelude::DataSchemaRef; +use common_datavalues::prelude::DataSchemaRefExt; +use common_datavalues::prelude::Series; +use common_datavalues::SeriesFrom; +use common_exception::Result; +use common_meta_api::ShareApi; +use common_meta_app::share::GetShareGrantTenantsReq; +use common_meta_app::share::ShareNameIdent; +use common_streams::DataBlockStream; +use common_streams::SendableDataBlockStream; + +use crate::interpreters::Interpreter; +use crate::sessions::QueryContext; +use crate::sessions::TableContext; +use crate::sql::plans::share::ShowGrantTenantsOfSharePlan; + +pub struct ShowGrantTenantsOfShareInterpreter { + ctx: Arc, + plan: ShowGrantTenantsOfSharePlan, +} + +impl ShowGrantTenantsOfShareInterpreter { + pub fn try_create(ctx: Arc, plan: ShowGrantTenantsOfSharePlan) -> Result { + Ok(ShowGrantTenantsOfShareInterpreter { ctx, plan }) + } +} + +#[async_trait::async_trait] +impl Interpreter for ShowGrantTenantsOfShareInterpreter { + fn name(&self) -> &str { + "ShowGrantTenantsOfShareInterpreter" + } + + fn schema(&self) -> DataSchemaRef { + self.plan.schema() + } + + async fn execute(&self) -> Result { + let user_mgr = self.ctx.get_user_manager(); + let meta_api = user_mgr.get_meta_store_client(); + let tenant = self.ctx.get_tenant(); + let req = GetShareGrantTenantsReq { + share_name: ShareNameIdent { + tenant, + share_name: self.plan.share_name.clone(), + }, + }; + let resp = meta_api.get_grant_tenants_of_share(req).await?; + if resp.accounts.is_empty() { + return Ok(Box::pin(DataBlockStream::create( + DataSchemaRefExt::create(vec![]), + None, + vec![], + ))); + } + + let desc_schema = self.plan.schema(); + + let mut granted_ons: Vec = vec![]; + let mut accounts: Vec = vec![]; + for account in resp.accounts { + granted_ons.push(account.grant_on.to_string()); + accounts.push(account.account.clone()); + } + + let block = DataBlock::create(desc_schema.clone(), vec![ + Series::from_data(granted_ons), + Series::from_data(accounts), + ]); + Ok(Box::pin(DataBlockStream::create(desc_schema, None, vec![ + block, + ]))) + } +} diff --git a/src/query/service/src/interpreters/interpreter_show_object_grant_privileges.rs b/src/query/service/src/interpreters/interpreter_show_object_grant_privileges.rs new file mode 100644 index 0000000000000..56260f83a868b --- /dev/null +++ b/src/query/service/src/interpreters/interpreter_show_object_grant_privileges.rs @@ -0,0 +1,89 @@ +// 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_datablocks::DataBlock; +use common_datavalues::prelude::DataSchemaRef; +use common_datavalues::prelude::DataSchemaRefExt; +use common_datavalues::prelude::Series; +use common_datavalues::SeriesFrom; +use common_exception::Result; +use common_meta_api::ShareApi; +use common_meta_app::share::GetObjectGrantPrivilegesReq; +use common_streams::DataBlockStream; +use common_streams::SendableDataBlockStream; + +use crate::interpreters::Interpreter; +use crate::sessions::QueryContext; +use crate::sessions::TableContext; +use crate::sql::plans::share::ShowObjectGrantPrivilegesPlan; + +pub struct ShowObjectGrantPrivilegesInterpreter { + ctx: Arc, + plan: ShowObjectGrantPrivilegesPlan, +} + +impl ShowObjectGrantPrivilegesInterpreter { + pub fn try_create(ctx: Arc, plan: ShowObjectGrantPrivilegesPlan) -> Result { + Ok(ShowObjectGrantPrivilegesInterpreter { ctx, plan }) + } +} + +#[async_trait::async_trait] +impl Interpreter for ShowObjectGrantPrivilegesInterpreter { + fn name(&self) -> &str { + "ShowObjectGrantPrivilegesInterpreter" + } + + fn schema(&self) -> DataSchemaRef { + self.plan.schema() + } + + async fn execute(&self) -> Result { + let user_mgr = self.ctx.get_user_manager(); + let meta_api = user_mgr.get_meta_store_client(); + let req = GetObjectGrantPrivilegesReq { + tenant: self.ctx.get_tenant(), + object: self.plan.object.clone(), + }; + let resp = meta_api.get_grant_privileges_of_object(req).await?; + if resp.privileges.is_empty() { + return Ok(Box::pin(DataBlockStream::create( + DataSchemaRefExt::create(vec![]), + None, + vec![], + ))); + } + let desc_schema = self.plan.schema(); + let mut share_names: Vec = vec![]; + let mut privileges: Vec = vec![]; + let mut created_ons: Vec = vec![]; + + for privilege in resp.privileges { + share_names.push(privilege.share_name); + privileges.push(privilege.privileges.to_string()); + created_ons.push(privilege.grant_on.to_string()); + } + + let block = DataBlock::create(desc_schema.clone(), vec![ + Series::from_data(created_ons), + Series::from_data(privileges), + Series::from_data(share_names), + ]); + Ok(Box::pin(DataBlockStream::create(desc_schema, None, vec![ + block, + ]))) + } +} diff --git a/src/query/service/src/interpreters/mod.rs b/src/query/service/src/interpreters/mod.rs index b9856818da32c..91dca3d9b8fdd 100644 --- a/src/query/service/src/interpreters/mod.rs +++ b/src/query/service/src/interpreters/mod.rs @@ -56,10 +56,12 @@ mod interpreter_share_drop; mod interpreter_share_grant_object; mod interpreter_share_revoke_object; mod interpreter_share_show; +mod interpreter_share_show_grant_tenants; mod interpreter_show_databases; mod interpreter_show_functions; mod interpreter_show_grants; mod interpreter_show_metrics; +mod interpreter_show_object_grant_privileges; mod interpreter_show_processlist; mod interpreter_show_roles; mod interpreter_show_settings; @@ -141,10 +143,12 @@ pub use interpreter_share_drop::DropShareInterpreter; pub use interpreter_share_grant_object::GrantShareObjectInterpreter; pub use interpreter_share_revoke_object::RevokeShareObjectInterpreter; pub use interpreter_share_show::ShowSharesInterpreter; +pub use interpreter_share_show_grant_tenants::ShowGrantTenantsOfShareInterpreter; pub use interpreter_show_databases::ShowDatabasesInterpreter; pub use interpreter_show_functions::ShowFunctionsInterpreter; pub use interpreter_show_grants::ShowGrantsInterpreter; pub use interpreter_show_metrics::ShowMetricsInterpreter; +pub use interpreter_show_object_grant_privileges::ShowObjectGrantPrivilegesInterpreter; pub use interpreter_show_processlist::ShowProcessListInterpreter; pub use interpreter_show_roles::ShowRolesInterpreter; pub use interpreter_show_settings::ShowSettingsInterpreter; diff --git a/src/query/service/src/servers/mysql/mysql_interactive_worker.rs b/src/query/service/src/servers/mysql/mysql_interactive_worker.rs index e55d93af9c1bd..7acc3dd2ed10c 100644 --- a/src/query/service/src/servers/mysql/mysql_interactive_worker.rs +++ b/src/query/service/src/servers/mysql/mysql_interactive_worker.rs @@ -69,6 +69,8 @@ fn has_result_set_by_plan(plan: &Plan) -> bool { | Plan::ShowCreateTable(_) | Plan::DescShare(_) | Plan::ShowShares(_) + | Plan::ShowObjectGrantPrivileges(_) + | Plan::ShowGrantTenantsOfShare(_) | Plan::DescribeTable(_) | Plan::ShowGrants(_) | Plan::ListStage(_) diff --git a/src/query/service/src/sql/planner/binder/ddl/share.rs b/src/query/service/src/sql/planner/binder/ddl/share.rs index 7e965ee1c2609..3c8c83dc633db 100644 --- a/src/query/service/src/sql/planner/binder/ddl/share.rs +++ b/src/query/service/src/sql/planner/binder/ddl/share.rs @@ -26,6 +26,8 @@ use crate::sql::plans::DropSharePlan; use crate::sql::plans::GrantShareObjectPlan; use crate::sql::plans::Plan; use crate::sql::plans::RevokeShareObjectPlan; +use crate::sql::plans::ShowGrantTenantsOfSharePlan; +use crate::sql::plans::ShowObjectGrantPrivilegesPlan; use crate::sql::plans::ShowSharesPlan; impl<'a> Binder { @@ -146,4 +148,28 @@ impl<'a> Binder { ) -> Result { Ok(Plan::ShowShares(Box::new(ShowSharesPlan {}))) } + + pub(in crate::sql::planner::binder) async fn bind_show_object_grant_privileges( + &mut self, + stmt: &ShowObjectGrantPrivilegesStmt, + ) -> Result { + let ShowObjectGrantPrivilegesStmt { object } = stmt; + + let plan = ShowObjectGrantPrivilegesPlan { + object: object.clone(), + }; + Ok(Plan::ShowObjectGrantPrivileges(Box::new(plan))) + } + + pub(in crate::sql::planner::binder) async fn bind_show_grants_of_share( + &mut self, + stmt: &ShowGrantsOfShareStmt, + ) -> Result { + let ShowGrantsOfShareStmt { share_name } = stmt; + + let plan = ShowGrantTenantsOfSharePlan { + share_name: share_name.clone(), + }; + Ok(Plan::ShowGrantTenantsOfShare(Box::new(plan))) + } } diff --git a/src/query/service/src/sql/planner/binder/mod.rs b/src/query/service/src/sql/planner/binder/mod.rs index d0aed2af18513..fffaa6eb40ddc 100644 --- a/src/query/service/src/sql/planner/binder/mod.rs +++ b/src/query/service/src/sql/planner/binder/mod.rs @@ -337,6 +337,12 @@ impl<'a> Binder { Statement::ShowShares(stmt) => { self.bind_show_shares(stmt).await? } + Statement::ShowObjectGrantPrivileges(stmt) => { + self.bind_show_object_grant_privileges(stmt).await? + } + Statement::ShowGrantsOfShare(stmt) => { + self.bind_show_grants_of_share(stmt).await? + } }; Ok(plan) } diff --git a/src/query/service/src/sql/planner/format/display_plan.rs b/src/query/service/src/sql/planner/format/display_plan.rs index 42de37fa6defb..d9d675f1c1492 100644 --- a/src/query/service/src/sql/planner/format/display_plan.rs +++ b/src/query/service/src/sql/planner/format/display_plan.rs @@ -100,6 +100,8 @@ impl Plan { Plan::AlterShareTenants(p) => Ok(format!("{:?}", p)), Plan::DescShare(p) => Ok(format!("{:?}", p)), Plan::ShowShares(p) => Ok(format!("{:?}", p)), + Plan::ShowObjectGrantPrivileges(p) => Ok(format!("{:?}", p)), + Plan::ShowGrantTenantsOfShare(p) => Ok(format!("{:?}", p)), } } } diff --git a/src/query/service/src/sql/planner/plans/mod.rs b/src/query/service/src/sql/planner/plans/mod.rs index 2ef4099376908..b9c0db46da89a 100644 --- a/src/query/service/src/sql/planner/plans/mod.rs +++ b/src/query/service/src/sql/planner/plans/mod.rs @@ -206,6 +206,8 @@ pub enum Plan { AlterShareTenants(Box), DescShare(Box), ShowShares(Box), + ShowObjectGrantPrivileges(Box), + ShowGrantTenantsOfShare(Box), } #[derive(Clone, Debug)] @@ -283,6 +285,8 @@ impl Display for Plan { Plan::AlterShareTenants(_) => write!(f, "AlterShareTenants"), Plan::DescShare(_) => write!(f, "DescShare"), Plan::ShowShares(_) => write!(f, "ShowShares"), + Plan::ShowObjectGrantPrivileges(_) => write!(f, "ShowObjectGrantPrivileges"), + Plan::ShowGrantTenantsOfShare(_) => write!(f, "ShowGrantTenantsOfShare"), } } } @@ -351,6 +355,8 @@ impl Plan { Plan::AlterShareTenants(plan) => plan.schema(), Plan::DescShare(plan) => plan.schema(), Plan::ShowShares(plan) => plan.schema(), + Plan::ShowObjectGrantPrivileges(plan) => plan.schema(), + Plan::ShowGrantTenantsOfShare(plan) => plan.schema(), } } } diff --git a/src/query/service/src/sql/planner/plans/share.rs b/src/query/service/src/sql/planner/plans/share.rs index 2004df8428073..ba91f114e2606 100644 --- a/src/query/service/src/sql/planner/plans/share.rs +++ b/src/query/service/src/sql/planner/plans/share.rs @@ -156,3 +156,34 @@ impl ShowSharesPlan { ])) } } + +// Show object grant privileges. +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, PartialEq, Eq)] +pub struct ShowObjectGrantPrivilegesPlan { + pub object: ShareGrantObjectName, +} + +impl ShowObjectGrantPrivilegesPlan { + pub fn schema(&self) -> DataSchemaRef { + Arc::new(DataSchema::new(vec![ + DataField::new("Granted_on", Vu8::to_data_type()), + DataField::new("Privilege", Vu8::to_data_type()), + DataField::new("Share_name", Vu8::to_data_type()), + ])) + } +} + +// Show grant tenants of share. +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, PartialEq, Eq)] +pub struct ShowGrantTenantsOfSharePlan { + pub share_name: String, +} + +impl ShowGrantTenantsOfSharePlan { + pub fn schema(&self) -> DataSchemaRef { + Arc::new(DataSchema::new(vec![ + DataField::new("Granted_on", Vu8::to_data_type()), + DataField::new("Account", Vu8::to_data_type()), + ])) + } +} diff --git a/tests/suites/0_stateless/06_show/06_0009_show_shares.result b/tests/suites/0_stateless/06_show/06_0009_show_shares.result index 390ce9918f048..28385f7739286 100644 --- a/tests/suites/0_stateless/06_show/06_0009_show_shares.result +++ b/tests/suites/0_stateless/06_show/06_0009_show_shares.result @@ -1 +1,5 @@ -yyyy-mm-dd HH:MM:SS OUTBOUND test_share test_tenant +yyyy-mm-dd HH:MM:SS OUTBOUND test_share show_shares test_tenant x comment +TABLE show_shares.test_tb yyyy-mm-dd HH:MM:SS +DATABASE show_shares yyyy-mm-dd HH:MM:SS +yyyy-mm-dd HH:MM:SS Usage test_share +yyyy-mm-dd HH:MM:SS Select test_share diff --git a/tests/suites/0_stateless/06_show/06_0009_show_shares.sql b/tests/suites/0_stateless/06_show/06_0009_show_shares.sql index cedf3cede33be..64728e4171ceb 100644 --- a/tests/suites/0_stateless/06_show/06_0009_show_shares.sql +++ b/tests/suites/0_stateless/06_show/06_0009_show_shares.sql @@ -1,11 +1,20 @@ DROP DATABASE IF EXISTS show_shares; +DROP SHARE IF EXISTS test_share; CREATE DATABASE show_shares; - USE show_shares; +DROP TABLE IF EXISTS test_tb; +create table test_tb (a int); -create share test_share; +create share test_share comment = 'comment'; +alter share test_share add tenants = x; +grant USAGE on DATABASE show_shares TO SHARE test_share; +grant SELECT on TABLE show_shares.test_tb TO SHARE test_share; show shares; +desc share test_share; +show grants on DATABASE show_shares; +show grants on TABLE show_shares.test_tb; - +DROP TABLE IF EXISTS test_tb; DROP DATABASE IF EXISTS show_shares; +DROP SHARE IF EXISTS test_share;