From 7f1da171500cc3726cd24735e0461681ffa40dc8 Mon Sep 17 00:00:00 2001 From: WaterWhisperer Date: Thu, 20 Nov 2025 09:39:35 +0800 Subject: [PATCH] feat: support alter database compaction options (#7251) Signed-off-by: WaterWhisperer --- src/common/meta/src/ddl/alter_database.rs | 43 ++++ src/common/meta/src/rpc/ddl.rs | 25 ++- .../common/alter/alter_database.result | 210 ++++++++++++++++++ .../common/alter/alter_database.sql | 58 +++++ 4 files changed, 332 insertions(+), 4 deletions(-) diff --git a/src/common/meta/src/ddl/alter_database.rs b/src/common/meta/src/ddl/alter_database.rs index 6e199cb92a..736459e533 100644 --- a/src/common/meta/src/ddl/alter_database.rs +++ b/src/common/meta/src/ddl/alter_database.rs @@ -47,6 +47,9 @@ fn build_new_schema_value( SetDatabaseOption::Ttl(ttl) => { value.ttl = Some(*ttl); } + SetDatabaseOption::Other(key, val) => { + value.extra_options.insert(key.clone(), val.clone()); + } } } } @@ -54,6 +57,9 @@ fn build_new_schema_value( for key in keys.0.iter() { match key { UnsetDatabaseOption::Ttl => value.ttl = None, + UnsetDatabaseOption::Other(key) => { + value.extra_options.remove(key); + } } } } @@ -234,4 +240,41 @@ mod tests { build_new_schema_value(current_schema_value, &unset_ttl_alter_kind).unwrap(); assert_eq!(new_schema_value.ttl, None); } + + #[test] + fn test_build_new_schema_value_with_compaction_options() { + let set_compaction = AlterDatabaseKind::SetDatabaseOptions(SetDatabaseOptions(vec![ + SetDatabaseOption::Other("compaction.type".to_string(), "twcs".to_string()), + SetDatabaseOption::Other("compaction.twcs.time_window".to_string(), "1d".to_string()), + ])); + + let current_schema_value = SchemaNameValue::default(); + let new_schema_value = + build_new_schema_value(current_schema_value.clone(), &set_compaction).unwrap(); + + assert_eq!( + new_schema_value.extra_options.get("compaction.type"), + Some(&"twcs".to_string()) + ); + assert_eq!( + new_schema_value + .extra_options + .get("compaction.twcs.time_window"), + Some(&"1d".to_string()) + ); + + let unset_compaction = AlterDatabaseKind::UnsetDatabaseOptions(UnsetDatabaseOptions(vec![ + UnsetDatabaseOption::Other("compaction.type".to_string()), + ])); + + let new_schema_value = build_new_schema_value(new_schema_value, &unset_compaction).unwrap(); + + assert_eq!(new_schema_value.extra_options.get("compaction.type"), None); + assert_eq!( + new_schema_value + .extra_options + .get("compaction.twcs.time_window"), + Some(&"1d".to_string()) + ); + } } diff --git a/src/common/meta/src/rpc/ddl.rs b/src/common/meta/src/rpc/ddl.rs index b9a871775f..2fe936e6fd 100644 --- a/src/common/meta/src/rpc/ddl.rs +++ b/src/common/meta/src/rpc/ddl.rs @@ -47,6 +47,7 @@ use serde_with::{DefaultOnNull, serde_as}; use session::context::{QueryContextBuilder, QueryContextRef}; use snafu::{OptionExt, ResultExt}; use table::metadata::{RawTableInfo, TableId}; +use table::requests::validate_database_option; use table::table_name::TableName; use table::table_reference::TableReference; @@ -1059,14 +1060,21 @@ impl TryFrom for SetDatabaseOption { type Error = error::Error; fn try_from(PbOption { key, value }: PbOption) -> Result { - match key.to_ascii_lowercase().as_str() { + let key_lower = key.to_ascii_lowercase(); + match key_lower.as_str() { TTL_KEY => { let ttl = DatabaseTimeToLive::from_humantime_or_str(&value) .map_err(|_| InvalidSetDatabaseOptionSnafu { key, value }.build())?; Ok(SetDatabaseOption::Ttl(ttl)) } - _ => InvalidSetDatabaseOptionSnafu { key, value }.fail(), + _ => { + if validate_database_option(&key_lower) { + Ok(SetDatabaseOption::Other(key_lower, value)) + } else { + InvalidSetDatabaseOptionSnafu { key, value }.fail() + } + } } } } @@ -1074,20 +1082,29 @@ impl TryFrom for SetDatabaseOption { #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] pub enum SetDatabaseOption { Ttl(DatabaseTimeToLive), + Other(String, String), } #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] pub enum UnsetDatabaseOption { Ttl, + Other(String), } impl TryFrom<&str> for UnsetDatabaseOption { type Error = error::Error; fn try_from(key: &str) -> Result { - match key.to_ascii_lowercase().as_str() { + let key_lower = key.to_ascii_lowercase(); + match key_lower.as_str() { TTL_KEY => Ok(UnsetDatabaseOption::Ttl), - _ => InvalidUnsetDatabaseOptionSnafu { key }.fail(), + _ => { + if validate_database_option(&key_lower) { + Ok(UnsetDatabaseOption::Other(key_lower)) + } else { + InvalidUnsetDatabaseOptionSnafu { key }.fail() + } + } } } } diff --git a/tests/cases/standalone/common/alter/alter_database.result b/tests/cases/standalone/common/alter/alter_database.result index 8ff458989e..911ef5ddfc 100644 --- a/tests/cases/standalone/common/alter/alter_database.result +++ b/tests/cases/standalone/common/alter/alter_database.result @@ -104,6 +104,216 @@ SHOW CREATE DATABASE alter_database; | alter_database | CREATE DATABASE IF NOT EXISTS alter_database | +----------------+----------------------------------------------+ +ALTER DATABASE alter_database SET 'compaction.type'='twcs'; + +Affected Rows: 0 + +SHOW CREATE DATABASE alter_database; + ++----------------+----------------------------------------------+ +| Database | Create Database | ++----------------+----------------------------------------------+ +| alter_database | CREATE DATABASE IF NOT EXISTS alter_database | +| | WITH( | +| | 'compaction.type' = 'twcs' | +| | ) | ++----------------+----------------------------------------------+ + +ALTER DATABASE alter_database SET 'compaction.twcs.time_window'='2h'; + +Affected Rows: 0 + +SHOW CREATE DATABASE alter_database; + ++----------------+----------------------------------------------+ +| Database | Create Database | ++----------------+----------------------------------------------+ +| alter_database | CREATE DATABASE IF NOT EXISTS alter_database | +| | WITH( | +| | 'compaction.twcs.time_window' = '2h', | +| | 'compaction.type' = 'twcs' | +| | ) | ++----------------+----------------------------------------------+ + +ALTER DATABASE alter_database SET 'compaction.twcs.trigger_file_num'='8'; + +Affected Rows: 0 + +ALTER DATABASE alter_database SET 'compaction.twcs.max_output_file_size'='512MB'; + +Affected Rows: 0 + +SHOW CREATE DATABASE alter_database; + ++----------------+-----------------------------------------------------+ +| Database | Create Database | ++----------------+-----------------------------------------------------+ +| alter_database | CREATE DATABASE IF NOT EXISTS alter_database | +| | WITH( | +| | 'compaction.twcs.max_output_file_size' = '512MB', | +| | 'compaction.twcs.time_window' = '2h', | +| | 'compaction.twcs.trigger_file_num' = '8', | +| | 'compaction.type' = 'twcs' | +| | ) | ++----------------+-----------------------------------------------------+ + +ALTER DATABASE alter_database SET 'compaction.twcs.time_window'='1d'; + +Affected Rows: 0 + +SHOW CREATE DATABASE alter_database; + ++----------------+-----------------------------------------------------+ +| Database | Create Database | ++----------------+-----------------------------------------------------+ +| alter_database | CREATE DATABASE IF NOT EXISTS alter_database | +| | WITH( | +| | 'compaction.twcs.max_output_file_size' = '512MB', | +| | 'compaction.twcs.time_window' = '1d', | +| | 'compaction.twcs.trigger_file_num' = '8', | +| | 'compaction.type' = 'twcs' | +| | ) | ++----------------+-----------------------------------------------------+ + +-- SQLNESS ARG restart=true +SHOW CREATE DATABASE alter_database; + ++----------------+-----------------------------------------------------+ +| Database | Create Database | ++----------------+-----------------------------------------------------+ +| alter_database | CREATE DATABASE IF NOT EXISTS alter_database | +| | WITH( | +| | 'compaction.twcs.max_output_file_size' = '512MB', | +| | 'compaction.twcs.time_window' = '1d', | +| | 'compaction.twcs.trigger_file_num' = '8', | +| | 'compaction.type' = 'twcs' | +| | ) | ++----------------+-----------------------------------------------------+ + +ALTER DATABASE alter_database UNSET 'compaction.twcs.trigger_file_num'; + +Affected Rows: 0 + +SHOW CREATE DATABASE alter_database; + ++----------------+-----------------------------------------------------+ +| Database | Create Database | ++----------------+-----------------------------------------------------+ +| alter_database | CREATE DATABASE IF NOT EXISTS alter_database | +| | WITH( | +| | 'compaction.twcs.max_output_file_size' = '512MB', | +| | 'compaction.twcs.time_window' = '1d', | +| | 'compaction.type' = 'twcs' | +| | ) | ++----------------+-----------------------------------------------------+ + +ALTER DATABASE alter_database UNSET 'compaction.twcs.time_window'; + +Affected Rows: 0 + +SHOW CREATE DATABASE alter_database; + ++----------------+-----------------------------------------------------+ +| Database | Create Database | ++----------------+-----------------------------------------------------+ +| alter_database | CREATE DATABASE IF NOT EXISTS alter_database | +| | WITH( | +| | 'compaction.twcs.max_output_file_size' = '512MB', | +| | 'compaction.type' = 'twcs' | +| | ) | ++----------------+-----------------------------------------------------+ + +ALTER DATABASE alter_database SET 'compaction.twcs.fallback_to_local'='true'; + +Affected Rows: 0 + +SHOW CREATE DATABASE alter_database; + ++----------------+-----------------------------------------------------+ +| Database | Create Database | ++----------------+-----------------------------------------------------+ +| alter_database | CREATE DATABASE IF NOT EXISTS alter_database | +| | WITH( | +| | 'compaction.twcs.fallback_to_local' = 'true', | +| | 'compaction.twcs.max_output_file_size' = '512MB', | +| | 'compaction.type' = 'twcs' | +| | ) | ++----------------+-----------------------------------------------------+ + +ALTER DATABASE alter_database UNSET 'compaction.type'; + +Affected Rows: 0 + +ALTER DATABASE alter_database UNSET 'compaction.twcs.max_output_file_size'; + +Affected Rows: 0 + +ALTER DATABASE alter_database UNSET 'compaction.twcs.fallback_to_local'; + +Affected Rows: 0 + +SHOW CREATE DATABASE alter_database; + ++----------------+----------------------------------------------+ +| Database | Create Database | ++----------------+----------------------------------------------+ +| alter_database | CREATE DATABASE IF NOT EXISTS alter_database | ++----------------+----------------------------------------------+ + +-- SQLNESS ARG restart=true +SHOW CREATE DATABASE alter_database; + ++----------------+----------------------------------------------+ +| Database | Create Database | ++----------------+----------------------------------------------+ +| alter_database | CREATE DATABASE IF NOT EXISTS alter_database | ++----------------+----------------------------------------------+ + +ALTER DATABASE alter_database SET 'invalid.compaction.option'='value'; + +Error: 1004(InvalidArguments), Invalid set database option, key: invalid.compaction.option, value: value + +ALTER DATABASE alter_database SET 'ttl'='1h'; + +Affected Rows: 0 + +ALTER DATABASE alter_database SET 'compaction.type'='twcs'; + +Affected Rows: 0 + +ALTER DATABASE alter_database SET 'compaction.twcs.time_window'='30m'; + +Affected Rows: 0 + +SHOW CREATE DATABASE alter_database; + ++----------------+----------------------------------------------+ +| Database | Create Database | ++----------------+----------------------------------------------+ +| alter_database | CREATE DATABASE IF NOT EXISTS alter_database | +| | WITH( | +| | 'compaction.twcs.time_window' = '30m', | +| | 'compaction.type' = 'twcs', | +| | ttl = '1h' | +| | ) | ++----------------+----------------------------------------------+ + +ALTER DATABASE alter_database UNSET 'ttl'; + +Affected Rows: 0 + +SHOW CREATE DATABASE alter_database; + ++----------------+----------------------------------------------+ +| Database | Create Database | ++----------------+----------------------------------------------+ +| alter_database | CREATE DATABASE IF NOT EXISTS alter_database | +| | WITH( | +| | 'compaction.twcs.time_window' = '30m', | +| | 'compaction.type' = 'twcs' | +| | ) | ++----------------+----------------------------------------------+ + DROP DATABASE alter_database; Affected Rows: 0 diff --git a/tests/cases/standalone/common/alter/alter_database.sql b/tests/cases/standalone/common/alter/alter_database.sql index f6491a24dd..1b2f75637a 100644 --- a/tests/cases/standalone/common/alter/alter_database.sql +++ b/tests/cases/standalone/common/alter/alter_database.sql @@ -32,5 +32,63 @@ SHOW CREATE DATABASE alter_database; -- SQLNESS ARG restart=true SHOW CREATE DATABASE alter_database; +ALTER DATABASE alter_database SET 'compaction.type'='twcs'; + +SHOW CREATE DATABASE alter_database; + +ALTER DATABASE alter_database SET 'compaction.twcs.time_window'='2h'; + +SHOW CREATE DATABASE alter_database; + +ALTER DATABASE alter_database SET 'compaction.twcs.trigger_file_num'='8'; + +ALTER DATABASE alter_database SET 'compaction.twcs.max_output_file_size'='512MB'; + +SHOW CREATE DATABASE alter_database; + +ALTER DATABASE alter_database SET 'compaction.twcs.time_window'='1d'; + +SHOW CREATE DATABASE alter_database; + +-- SQLNESS ARG restart=true +SHOW CREATE DATABASE alter_database; + +ALTER DATABASE alter_database UNSET 'compaction.twcs.trigger_file_num'; + +SHOW CREATE DATABASE alter_database; + +ALTER DATABASE alter_database UNSET 'compaction.twcs.time_window'; + +SHOW CREATE DATABASE alter_database; + +ALTER DATABASE alter_database SET 'compaction.twcs.fallback_to_local'='true'; + +SHOW CREATE DATABASE alter_database; + +ALTER DATABASE alter_database UNSET 'compaction.type'; + +ALTER DATABASE alter_database UNSET 'compaction.twcs.max_output_file_size'; + +ALTER DATABASE alter_database UNSET 'compaction.twcs.fallback_to_local'; + +SHOW CREATE DATABASE alter_database; + +-- SQLNESS ARG restart=true +SHOW CREATE DATABASE alter_database; + +ALTER DATABASE alter_database SET 'invalid.compaction.option'='value'; + +ALTER DATABASE alter_database SET 'ttl'='1h'; + +ALTER DATABASE alter_database SET 'compaction.type'='twcs'; + +ALTER DATABASE alter_database SET 'compaction.twcs.time_window'='30m'; + +SHOW CREATE DATABASE alter_database; + +ALTER DATABASE alter_database UNSET 'ttl'; + +SHOW CREATE DATABASE alter_database; + DROP DATABASE alter_database;