Skip to content

Commit ff04924

Browse files
authored
feat(query): ALTER TABLE Row Access Policy (#18540)
1 parent a139283 commit ff04924

File tree

68 files changed

+1452
-16
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+1452
-16
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/common/exception/src/exception_code.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,8 @@ build_exceptions! {
285285
EmptyShareEndpointConfig(1130),
286286
/// Unknown row policy
287287
UnknownRowAccessPolicy(1131),
288+
/// Alter Table error
289+
AlterTableError(1132),
288290
}
289291

290292
// Sequence Errors [1124-1126, 3101]

src/meta/api/src/errors.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2021 Datafuse Labs
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use databend_common_exception::ErrorCode;
16+
17+
/// Table logic error, unrelated to the backend service providing Table management, or dependent component.
18+
#[derive(Clone, Debug, thiserror::Error)]
19+
pub enum TableError {
20+
// NOTE: do not expose tenant in a for-user error message.
21+
#[error("Alter table with error {context}")]
22+
AlterTableError { tenant: String, context: String },
23+
#[error("Unknown table id {table_id}, {context}")]
24+
UnknownTableId {
25+
tenant: String,
26+
table_id: u64,
27+
context: String,
28+
},
29+
}
30+
31+
impl From<TableError> for ErrorCode {
32+
fn from(value: TableError) -> Self {
33+
let s = value.to_string();
34+
match value {
35+
TableError::AlterTableError { .. } => ErrorCode::AlterTableError(s),
36+
TableError::UnknownTableId { .. } => ErrorCode::UnknownTableId(s),
37+
}
38+
}
39+
}

src/meta/api/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pub mod txn_backoff;
3636
pub mod util;
3737

3838
pub mod crud;
39+
mod errors;
3940
mod row_access_policy_api;
4041
mod row_access_policy_api_impl;
4142
mod sequence_api_impl;

src/meta/api/src/row_access_policy_api.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,5 @@ pub trait RowAccessPolicyApi: Send + Sync {
4444
async fn get_row_access(
4545
&self,
4646
name_ident: &RowAccessPolicyNameIdent,
47-
) -> Result<Option<SeqV<RowAccessPolicyMeta>>, MetaError>;
47+
) -> Result<Option<(SeqV<RowAccessPolicyId>, SeqV<RowAccessPolicyMeta>)>, MetaError>;
4848
}

src/meta/api/src/row_access_policy_api_impl.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,11 +157,11 @@ impl<KV: kvapi::KVApi<Error = MetaError>> RowAccessPolicyApi for KV {
157157
async fn get_row_access(
158158
&self,
159159
name_ident: &RowAccessPolicyNameIdent,
160-
) -> Result<Option<SeqV<RowAccessPolicyMeta>>, MetaError> {
160+
) -> Result<Option<(SeqV<RowAccessPolicyId>, SeqV<RowAccessPolicyMeta>)>, MetaError> {
161161
debug!(req :? =(&name_ident); "RowAccessPolicyApi: {}", func_name!());
162162

163163
let res = self.get_id_and_value(name_ident).await?;
164164

165-
Ok(res.map(|(_, seq_meta)| seq_meta))
165+
Ok(res)
166166
}
167167
}

src/meta/api/src/schema_api.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ use databend_common_meta_app::schema::RenameTableReply;
7676
use databend_common_meta_app::schema::RenameTableReq;
7777
use databend_common_meta_app::schema::SetTableColumnMaskPolicyReply;
7878
use databend_common_meta_app::schema::SetTableColumnMaskPolicyReq;
79+
use databend_common_meta_app::schema::SetTableRowAccessPolicyReply;
80+
use databend_common_meta_app::schema::SetTableRowAccessPolicyReq;
7981
use databend_common_meta_app::schema::TableId;
8082
use databend_common_meta_app::schema::TableIdHistoryIdent;
8183
use databend_common_meta_app::schema::TableInfo;
@@ -100,6 +102,7 @@ use databend_common_meta_types::MetaId;
100102
use databend_common_meta_types::SeqV;
101103
use databend_common_proto_conv::FromToProto;
102104

105+
use crate::errors::TableError;
103106
use crate::kv_app_error::KVAppError;
104107
use crate::meta_txn_error::MetaTxnError;
105108

@@ -284,6 +287,11 @@ pub trait SchemaApi: Send + Sync {
284287
req: SetTableColumnMaskPolicyReq,
285288
) -> Result<SetTableColumnMaskPolicyReply, KVAppError>;
286289

290+
async fn set_table_row_access_policy(
291+
&self,
292+
req: SetTableRowAccessPolicyReq,
293+
) -> Result<Result<SetTableRowAccessPolicyReply, TableError>, MetaTxnError>;
294+
287295
async fn create_table_index(&self, req: CreateTableIndexReq) -> Result<(), KVAppError>;
288296

289297
async fn drop_table_index(&self, req: DropTableIndexReq) -> Result<(), KVAppError>;

src/meta/api/src/schema_api_impl.rs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ use databend_common_meta_app::app_error::VirtualColumnIdOutBound;
6161
use databend_common_meta_app::app_error::VirtualColumnTooMany;
6262
use databend_common_meta_app::data_mask::MaskPolicyTableIdListIdent;
6363
use databend_common_meta_app::id_generator::IdGenerator;
64+
use databend_common_meta_app::row_access_policy::row_access_policy_table_id_ident::RowAccessPolicyIdTableId;
65+
use databend_common_meta_app::row_access_policy::RowAccessPolicyTableId;
66+
use databend_common_meta_app::row_access_policy::RowAccessPolicyTableIdIdent;
6467
use databend_common_meta_app::schema::catalog_id_ident::CatalogId;
6568
use databend_common_meta_app::schema::catalog_name_ident::CatalogNameIdentRaw;
6669
use databend_common_meta_app::schema::database_name_ident::DatabaseNameIdent;
@@ -147,6 +150,9 @@ use databend_common_meta_app::schema::RenameTableReq;
147150
use databend_common_meta_app::schema::SetTableColumnMaskPolicyAction;
148151
use databend_common_meta_app::schema::SetTableColumnMaskPolicyReply;
149152
use databend_common_meta_app::schema::SetTableColumnMaskPolicyReq;
153+
use databend_common_meta_app::schema::SetTableRowAccessPolicyAction;
154+
use databend_common_meta_app::schema::SetTableRowAccessPolicyReply;
155+
use databend_common_meta_app::schema::SetTableRowAccessPolicyReq;
150156
use databend_common_meta_app::schema::TableCopiedFileNameIdent;
151157
use databend_common_meta_app::schema::TableId;
152158
use databend_common_meta_app::schema::TableIdHistoryIdent;
@@ -207,6 +213,7 @@ use ConditionResult::Eq;
207213
use crate::assert_table_exist;
208214
use crate::db_has_to_exist;
209215
use crate::deserialize_struct;
216+
use crate::errors::TableError;
210217
use crate::fetch_id;
211218
use crate::get_u64_value;
212219
use crate::kv_app_error::KVAppError;
@@ -2469,6 +2476,95 @@ impl<KV: kvapi::KVApi<Error = MetaError> + ?Sized> SchemaApi for KV {
24692476
}
24702477
}
24712478

2479+
#[logcall::logcall]
2480+
#[fastrace::trace]
2481+
async fn set_table_row_access_policy(
2482+
&self,
2483+
req: SetTableRowAccessPolicyReq,
2484+
) -> Result<Result<SetTableRowAccessPolicyReply, TableError>, MetaTxnError> {
2485+
debug!(req :? =(&req); "SchemaApi: {}", func_name!());
2486+
let tbid = TableId {
2487+
table_id: req.table_id,
2488+
};
2489+
2490+
let mut trials = txn_backoff(None, func_name!());
2491+
loop {
2492+
trials.next().unwrap()?.await;
2493+
2494+
let seq_meta = self.get_pb(&tbid).await?;
2495+
2496+
debug!(ident :% =(&tbid); "set_table_row_access_policy");
2497+
2498+
let Some(seq_meta) = seq_meta else {
2499+
return Ok(Err(TableError::UnknownTableId {
2500+
tenant: req.tenant.tenant_name().to_string(),
2501+
table_id: req.table_id,
2502+
context: "set_table_row_access_policy".to_string(),
2503+
}));
2504+
};
2505+
2506+
// upsert row access policy
2507+
let table_meta = seq_meta.data;
2508+
let mut new_table_meta = table_meta.clone();
2509+
let id = RowAccessPolicyIdTableId {
2510+
policy_id: req.policy_id,
2511+
table_id: req.table_id,
2512+
};
2513+
let ident = RowAccessPolicyTableIdIdent::new_generic(req.tenant.clone(), id);
2514+
2515+
let mut txn_req = TxnRequest::default();
2516+
2517+
txn_req
2518+
.condition
2519+
.push(txn_cond_seq(&tbid, Eq, seq_meta.seq));
2520+
match &req.action {
2521+
SetTableRowAccessPolicyAction::Set(new_mask_name) => {
2522+
if table_meta.row_access_policy.is_some() {
2523+
return Ok(Err(TableError::AlterTableError {
2524+
tenant: req.tenant.tenant_name().to_string(),
2525+
context: "Table already has a ROW_ACCESS_POLICY. Only one ROW_ACCESS_POLICY is allowed at a time.".to_string(),
2526+
}));
2527+
}
2528+
new_table_meta.row_access_policy = Some(new_mask_name.to_string());
2529+
txn_req.if_then = vec![
2530+
txn_op_put(&tbid, serialize_struct(&new_table_meta)?), /* tb_id -> tb_meta row access policy Some */
2531+
txn_op_put(&ident, serialize_struct(&RowAccessPolicyTableId {})?), /* add policy_tb_id */
2532+
];
2533+
}
2534+
SetTableRowAccessPolicyAction::Unset(old_policy) => {
2535+
// drop row access policy and table does not have row access policy
2536+
if let Some(policy) = &table_meta.row_access_policy {
2537+
if policy != old_policy {
2538+
return Ok(Err(TableError::AlterTableError {
2539+
tenant: req.tenant.tenant_name().to_string(),
2540+
context: format!("Unknown row access policy {} on table", policy),
2541+
}));
2542+
}
2543+
} else {
2544+
return Ok(Ok(SetTableRowAccessPolicyReply {}));
2545+
}
2546+
new_table_meta.row_access_policy = None;
2547+
txn_req.if_then = vec![
2548+
txn_op_put(&tbid, serialize_struct(&new_table_meta)?), /* tb_id -> tb_meta row access policy None */
2549+
txn_op_del(&ident), // table drop row access policy, del policy_tb_id
2550+
];
2551+
}
2552+
}
2553+
2554+
let (succ, _responses) = send_txn(self, txn_req).await?;
2555+
2556+
debug!(
2557+
id :? =(&tbid),
2558+
succ = succ;
2559+
"set_table_row_access_policy"
2560+
);
2561+
2562+
if succ {
2563+
return Ok(Ok(SetTableRowAccessPolicyReply {}));
2564+
}
2565+
}
2566+
}
2567+
24722568
#[logcall::logcall]
24732569
#[fastrace::trace]
24742570
async fn create_table_index(&self, req: CreateTableIndexReq) -> Result<(), KVAppError> {

0 commit comments

Comments
 (0)