diff --git a/Cargo.lock b/Cargo.lock
index e57a6542af..534b8c465a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -12197,6 +12197,7 @@ dependencies = [
"arbitrary",
"async-trait",
"chrono",
+ "common-base",
"common-error",
"common-macro",
"common-query",
diff --git a/src/common/base/src/readable_size.rs b/src/common/base/src/readable_size.rs
index 21908526c7..4298989291 100644
--- a/src/common/base/src/readable_size.rs
+++ b/src/common/base/src/readable_size.rs
@@ -19,7 +19,7 @@ pub const GIB: u64 = MIB * BINARY_DATA_MAGNITUDE;
pub const TIB: u64 = GIB * BINARY_DATA_MAGNITUDE;
pub const PIB: u64 = TIB * BINARY_DATA_MAGNITUDE;
-#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd)]
+#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Default)]
pub struct ReadableSize(pub u64);
impl ReadableSize {
diff --git a/src/sql/src/statements/alter.rs b/src/sql/src/statements/alter.rs
index 174bdbbdc3..df148ae5b6 100644
--- a/src/sql/src/statements/alter.rs
+++ b/src/sql/src/statements/alter.rs
@@ -72,29 +72,20 @@ pub enum AlterTableOperation {
target_type: DataType,
},
/// `SET
= `
- SetTableOptions {
- options: Vec,
- },
- UnsetTableOptions {
- keys: Vec,
- },
+ SetTableOptions { options: Vec },
+ /// `UNSET `
+ UnsetTableOptions { keys: Vec },
/// `DROP COLUMN `
- DropColumn {
- name: Ident,
- },
+ DropColumn { name: Ident },
/// `RENAME `
- RenameTable {
- new_table_name: String,
- },
+ RenameTable { new_table_name: String },
/// `MODIFY COLUMN SET FULLTEXT [WITH ]`
SetColumnFulltext {
column_name: Ident,
options: FulltextOptions,
},
/// `MODIFY COLUMN UNSET FULLTEXT`
- UnsetColumnFulltext {
- column_name: Ident,
- },
+ UnsetColumnFulltext { column_name: Ident },
}
impl Display for AlterTableOperation {
diff --git a/tests-fuzz/Cargo.toml b/tests-fuzz/Cargo.toml
index cbac9df713..c408992bd5 100644
--- a/tests-fuzz/Cargo.toml
+++ b/tests-fuzz/Cargo.toml
@@ -18,6 +18,7 @@ unstable = ["nix"]
arbitrary = { version = "1.3.0", features = ["derive"] }
async-trait = { workspace = true }
chrono = { workspace = true }
+common-base = { workspace = true }
common-error = { workspace = true }
common-macro = { workspace = true }
common-query = { workspace = true }
@@ -67,14 +68,14 @@ dotenv.workspace = true
[[bin]]
name = "fuzz_create_table"
-path = "targets/fuzz_create_table.rs"
+path = "targets/ddl/fuzz_create_table.rs"
test = false
bench = false
doc = false
[[bin]]
name = "fuzz_create_logical_table"
-path = "targets/fuzz_create_logical_table.rs"
+path = "targets/ddl/fuzz_create_logical_table.rs"
test = false
bench = false
doc = false
@@ -95,21 +96,21 @@ doc = false
[[bin]]
name = "fuzz_alter_table"
-path = "targets/fuzz_alter_table.rs"
+path = "targets/ddl/fuzz_alter_table.rs"
test = false
bench = false
doc = false
[[bin]]
name = "fuzz_alter_logical_table"
-path = "targets/fuzz_alter_logical_table.rs"
+path = "targets/ddl/fuzz_alter_logical_table.rs"
test = false
bench = false
doc = false
[[bin]]
name = "fuzz_create_database"
-path = "targets/fuzz_create_database.rs"
+path = "targets/ddl/fuzz_create_database.rs"
test = false
bench = false
doc = false
diff --git a/tests-fuzz/src/context.rs b/tests-fuzz/src/context.rs
index 8cfd0ca9fa..d0d5dee72d 100644
--- a/tests-fuzz/src/context.rs
+++ b/tests-fuzz/src/context.rs
@@ -21,7 +21,7 @@ use snafu::{ensure, OptionExt};
use crate::error::{self, Result};
use crate::generator::Random;
-use crate::ir::alter_expr::AlterTableOperation;
+use crate::ir::alter_expr::{AlterTableOperation, AlterTableOption};
use crate::ir::{AlterTableExpr, Column, CreateTableExpr, Ident};
pub type TableContextRef = Arc;
@@ -35,6 +35,7 @@ pub struct TableContext {
// GreptimeDB specific options
pub partition: Option,
pub primary_keys: Vec,
+ pub table_options: Vec,
}
impl From<&CreateTableExpr> for TableContext {
@@ -52,6 +53,7 @@ impl From<&CreateTableExpr> for TableContext {
columns: columns.clone(),
partition: partition.clone(),
primary_keys: primary_keys.clone(),
+ table_options: vec![],
}
}
}
@@ -64,7 +66,7 @@ impl TableContext {
/// Applies the [AlterTableExpr].
pub fn alter(mut self, expr: AlterTableExpr) -> Result {
- match expr.alter_options {
+ match expr.alter_kinds {
AlterTableOperation::AddColumn { column, location } => {
ensure!(
!self.columns.iter().any(|col| col.name == column.name),
@@ -140,6 +142,25 @@ impl TableContext {
}
Ok(self)
}
+ AlterTableOperation::SetTableOptions { options } => {
+ for option in options {
+ if let Some(idx) = self
+ .table_options
+ .iter()
+ .position(|opt| opt.key() == option.key())
+ {
+ self.table_options[idx] = option;
+ } else {
+ self.table_options.push(option);
+ }
+ }
+ Ok(self)
+ }
+ AlterTableOperation::UnsetTableOptions { keys } => {
+ self.table_options
+ .retain(|opt| !keys.contains(&opt.key().to_string()));
+ Ok(self)
+ }
}
}
@@ -171,10 +192,11 @@ impl TableContext {
#[cfg(test)]
mod tests {
use common_query::AddColumnLocation;
+ use common_time::Duration;
use datatypes::data_type::ConcreteDataType;
use super::TableContext;
- use crate::ir::alter_expr::AlterTableOperation;
+ use crate::ir::alter_expr::{AlterTableOperation, AlterTableOption, Ttl};
use crate::ir::create_expr::ColumnOption;
use crate::ir::{AlterTableExpr, Column, Ident};
@@ -185,11 +207,12 @@ mod tests {
columns: vec![],
partition: None,
primary_keys: vec![],
+ table_options: vec![],
};
// Add a column
let expr = AlterTableExpr {
table_name: "foo".into(),
- alter_options: AlterTableOperation::AddColumn {
+ alter_kinds: AlterTableOperation::AddColumn {
column: Column {
name: "a".into(),
column_type: ConcreteDataType::timestamp_microsecond_datatype(),
@@ -205,7 +228,7 @@ mod tests {
// Add a column at first
let expr = AlterTableExpr {
table_name: "foo".into(),
- alter_options: AlterTableOperation::AddColumn {
+ alter_kinds: AlterTableOperation::AddColumn {
column: Column {
name: "b".into(),
column_type: ConcreteDataType::timestamp_microsecond_datatype(),
@@ -221,7 +244,7 @@ mod tests {
// Add a column after "b"
let expr = AlterTableExpr {
table_name: "foo".into(),
- alter_options: AlterTableOperation::AddColumn {
+ alter_kinds: AlterTableOperation::AddColumn {
column: Column {
name: "c".into(),
column_type: ConcreteDataType::timestamp_microsecond_datatype(),
@@ -239,10 +262,32 @@ mod tests {
// Drop the column "b"
let expr = AlterTableExpr {
table_name: "foo".into(),
- alter_options: AlterTableOperation::DropColumn { name: "b".into() },
+ alter_kinds: AlterTableOperation::DropColumn { name: "b".into() },
};
let table_ctx = table_ctx.alter(expr).unwrap();
assert_eq!(table_ctx.columns[1].name, Ident::new("a"));
assert_eq!(table_ctx.primary_keys, vec![0, 1]);
+
+ // Set table options
+ let ttl_option = AlterTableOption::Ttl(Ttl::Duration(Duration::new_second(60)));
+ let expr = AlterTableExpr {
+ table_name: "foo".into(),
+ alter_kinds: AlterTableOperation::SetTableOptions {
+ options: vec![ttl_option.clone()],
+ },
+ };
+ let table_ctx = table_ctx.alter(expr).unwrap();
+ assert_eq!(table_ctx.table_options.len(), 1);
+ assert_eq!(table_ctx.table_options[0], ttl_option);
+
+ // Unset table options
+ let expr = AlterTableExpr {
+ table_name: "foo".into(),
+ alter_kinds: AlterTableOperation::UnsetTableOptions {
+ keys: vec![ttl_option.key().to_string()],
+ },
+ };
+ let table_ctx = table_ctx.alter(expr).unwrap();
+ assert_eq!(table_ctx.table_options.len(), 0);
}
}
diff --git a/tests-fuzz/src/generator/alter_expr.rs b/tests-fuzz/src/generator/alter_expr.rs
index 03aed702fb..0c5a628999 100644
--- a/tests-fuzz/src/generator/alter_expr.rs
+++ b/tests-fuzz/src/generator/alter_expr.rs
@@ -14,17 +14,19 @@
use std::marker::PhantomData;
+use common_base::readable_size::ReadableSize;
use common_query::AddColumnLocation;
use datatypes::data_type::ConcreteDataType;
use derive_builder::Builder;
use rand::Rng;
use snafu::ensure;
+use strum::IntoEnumIterator;
use crate::context::TableContextRef;
use crate::error::{self, Error, Result};
use crate::fake::WordGenerator;
use crate::generator::{ColumnOptionGenerator, ConcreteDataTypeGenerator, Generator, Random};
-use crate::ir::alter_expr::{AlterTableExpr, AlterTableOperation};
+use crate::ir::alter_expr::{AlterTableExpr, AlterTableOperation, AlterTableOption, Ttl};
use crate::ir::create_expr::ColumnOption;
use crate::ir::{
droppable_columns, generate_columns, generate_random_value, modifiable_columns, Column,
@@ -107,7 +109,7 @@ impl Generator for AlterExprAddColumnGenera
.remove(0);
Ok(AlterTableExpr {
table_name: self.table_ctx.name.clone(),
- alter_options: AlterTableOperation::AddColumn { column, location },
+ alter_kinds: AlterTableOperation::AddColumn { column, location },
})
}
}
@@ -130,7 +132,7 @@ impl Generator for AlterExprDropColumnGenerator {
let name = droppable[rng.gen_range(0..droppable.len())].name.clone();
Ok(AlterTableExpr {
table_name: self.table_ctx.name.clone(),
- alter_options: AlterTableOperation::DropColumn { name },
+ alter_kinds: AlterTableOperation::DropColumn { name },
})
}
}
@@ -153,7 +155,7 @@ impl Generator for AlterExprRenameGenerator {
.generate_unique_table_name(rng, self.name_generator.as_ref());
Ok(AlterTableExpr {
table_name: self.table_ctx.name.clone(),
- alter_options: AlterTableOperation::RenameTable { new_table_name },
+ alter_kinds: AlterTableOperation::RenameTable { new_table_name },
})
}
}
@@ -180,7 +182,7 @@ impl Generator for AlterExprModifyDataTypeGenerator Generator for AlterExprModifyDataTypeGenerator {
+ table_ctx: TableContextRef,
+ #[builder(default)]
+ _phantom: PhantomData,
+}
+
+impl Generator for AlterExprSetTableOptionsGenerator {
+ type Error = Error;
+
+ fn generate(&self, rng: &mut R) -> Result {
+ let all_options = AlterTableOption::iter().collect::>();
+ // Generate random distinct options
+ let mut option_templates_idx = vec![];
+ for _ in 1..rng.gen_range(2..=all_options.len()) {
+ let option = rng.gen_range(0..all_options.len());
+ if !option_templates_idx.contains(&option) {
+ option_templates_idx.push(option);
+ }
+ }
+ let options = option_templates_idx
+ .iter()
+ .map(|idx| match all_options[*idx] {
+ AlterTableOption::Ttl(_) => {
+ let ttl_type = rng.gen_range(0..3);
+ match ttl_type {
+ 0 => {
+ let duration: u32 = rng.gen();
+ AlterTableOption::Ttl(Ttl::Duration((duration as i64).into()))
+ }
+ 1 => AlterTableOption::Ttl(Ttl::Instant),
+ 2 => AlterTableOption::Ttl(Ttl::Forever),
+ _ => unreachable!(),
+ }
+ }
+ AlterTableOption::TwcsTimeWindow(_) => {
+ let time_window: u32 = rng.gen();
+ AlterTableOption::TwcsTimeWindow((time_window as i64).into())
+ }
+ AlterTableOption::TwcsMaxOutputFileSize(_) => {
+ let max_output_file_size: u64 = rng.gen();
+ AlterTableOption::TwcsMaxOutputFileSize(ReadableSize(max_output_file_size))
+ }
+ AlterTableOption::TwcsMaxInactiveWindowRuns(_) => {
+ let max_inactive_window_runs: u64 = rng.gen();
+ AlterTableOption::TwcsMaxInactiveWindowRuns(max_inactive_window_runs)
+ }
+ AlterTableOption::TwcsMaxActiveWindowFiles(_) => {
+ let max_active_window_files: u64 = rng.gen();
+ AlterTableOption::TwcsMaxActiveWindowFiles(max_active_window_files)
+ }
+ AlterTableOption::TwcsMaxActiveWindowRuns(_) => {
+ let max_active_window_runs: u64 = rng.gen();
+ AlterTableOption::TwcsMaxActiveWindowRuns(max_active_window_runs)
+ }
+ AlterTableOption::TwcsMaxInactiveWindowFiles(_) => {
+ let max_inactive_window_files: u64 = rng.gen();
+ AlterTableOption::TwcsMaxInactiveWindowFiles(max_inactive_window_files)
+ }
+ })
+ .collect();
+ Ok(AlterTableExpr {
+ table_name: self.table_ctx.name.clone(),
+ alter_kinds: AlterTableOperation::SetTableOptions { options },
+ })
+ }
+}
+
+/// Generates the [AlterTableOperation::UnsetTableOptions] of [AlterTableExpr].
+#[derive(Builder)]
+#[builder(pattern = "owned")]
+pub struct AlterExprUnsetTableOptionsGenerator {
+ table_ctx: TableContextRef,
+ #[builder(default)]
+ _phantom: PhantomData,
+}
+
+impl Generator for AlterExprUnsetTableOptionsGenerator {
+ type Error = Error;
+
+ fn generate(&self, rng: &mut R) -> Result {
+ let all_options = AlterTableOption::iter().collect::>();
+ // Generate random distinct options
+ let mut option_templates_idx = vec![];
+ for _ in 1..rng.gen_range(2..=all_options.len()) {
+ let option = rng.gen_range(0..all_options.len());
+ if !option_templates_idx.contains(&option) {
+ option_templates_idx.push(option);
+ }
+ }
+ let options = option_templates_idx
+ .iter()
+ .map(|idx| all_options[*idx].key().to_string())
+ .collect();
+ Ok(AlterTableExpr {
+ table_name: self.table_ctx.name.clone(),
+ alter_kinds: AlterTableOperation::UnsetTableOptions { keys: options },
+ })
+ }
+}
+
#[cfg(test)]
mod tests {
use std::sync::Arc;
@@ -220,7 +325,7 @@ mod tests {
.generate(&mut rng)
.unwrap();
let serialized = serde_json::to_string(&expr).unwrap();
- let expected = r#"{"table_name":{"value":"animI","quote_style":null},"alter_options":{"AddColumn":{"column":{"name":{"value":"velit","quote_style":null},"column_type":{"Int32":{}},"options":[{"DefaultValue":{"Int32":1606462472}}]},"location":null}}}"#;
+ let expected = r#"{"table_name":{"value":"animI","quote_style":null},"alter_kinds":{"AddColumn":{"column":{"name":{"value":"velit","quote_style":null},"column_type":{"Int32":{}},"options":[{"DefaultValue":{"Int32":1606462472}}]},"location":null}}}"#;
assert_eq!(expected, serialized);
let expr = AlterExprRenameGeneratorBuilder::default()
@@ -230,7 +335,7 @@ mod tests {
.generate(&mut rng)
.unwrap();
let serialized = serde_json::to_string(&expr).unwrap();
- let expected = r#"{"table_name":{"value":"animI","quote_style":null},"alter_options":{"RenameTable":{"new_table_name":{"value":"nihil","quote_style":null}}}}"#;
+ let expected = r#"{"table_name":{"value":"animI","quote_style":null},"alter_kinds":{"RenameTable":{"new_table_name":{"value":"nihil","quote_style":null}}}}"#;
assert_eq!(expected, serialized);
let expr = AlterExprDropColumnGeneratorBuilder::default()
@@ -240,17 +345,37 @@ mod tests {
.generate(&mut rng)
.unwrap();
let serialized = serde_json::to_string(&expr).unwrap();
- let expected = r#"{"table_name":{"value":"animI","quote_style":null},"alter_options":{"DropColumn":{"name":{"value":"cUmquE","quote_style":null}}}}"#;
+ let expected = r#"{"table_name":{"value":"animI","quote_style":null},"alter_kinds":{"DropColumn":{"name":{"value":"cUmquE","quote_style":null}}}}"#;
assert_eq!(expected, serialized);
let expr = AlterExprModifyDataTypeGeneratorBuilder::default()
+ .table_ctx(table_ctx.clone())
+ .build()
+ .unwrap()
+ .generate(&mut rng)
+ .unwrap();
+ let serialized = serde_json::to_string(&expr).unwrap();
+ let expected = r#"{"table_name":{"value":"animI","quote_style":null},"alter_kinds":{"ModifyDataType":{"column":{"name":{"value":"toTAm","quote_style":null},"column_type":{"Int64":{}},"options":[]}}}}"#;
+ assert_eq!(expected, serialized);
+
+ let expr = AlterExprSetTableOptionsGeneratorBuilder::default()
+ .table_ctx(table_ctx.clone())
+ .build()
+ .unwrap()
+ .generate(&mut rng)
+ .unwrap();
+ let serialized = serde_json::to_string(&expr).unwrap();
+ let expected = r#"{"table_name":{"value":"animI","quote_style":null},"alter_kinds":{"SetTableOptions":{"options":[{"TwcsMaxActiveWindowRuns":14908016120444947142},{"TwcsMaxActiveWindowFiles":5840340123887173415},{"TwcsMaxOutputFileSize":17740311466571102265}]}}}"#;
+ assert_eq!(expected, serialized);
+
+ let expr = AlterExprUnsetTableOptionsGeneratorBuilder::default()
.table_ctx(table_ctx)
.build()
.unwrap()
.generate(&mut rng)
.unwrap();
let serialized = serde_json::to_string(&expr).unwrap();
- let expected = r#"{"table_name":{"value":"animI","quote_style":null},"alter_options":{"ModifyDataType":{"column":{"name":{"value":"toTAm","quote_style":null},"column_type":{"Int64":{}},"options":[]}}}}"#;
+ let expected = r#"{"table_name":{"value":"animI","quote_style":null},"alter_kinds":{"UnsetTableOptions":{"keys":["compaction.twcs.max_active_window_runs"]}}}"#;
assert_eq!(expected, serialized);
}
}
diff --git a/tests-fuzz/src/ir.rs b/tests-fuzz/src/ir.rs
index b9d13ca9fb..ae6edd595c 100644
--- a/tests-fuzz/src/ir.rs
+++ b/tests-fuzz/src/ir.rs
@@ -24,7 +24,7 @@ use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::Duration;
-pub use alter_expr::AlterTableExpr;
+pub use alter_expr::{AlterTableExpr, AlterTableOption};
use common_time::timestamp::TimeUnit;
use common_time::{Date, DateTime, Timestamp};
pub use create_expr::{CreateDatabaseExpr, CreateTableExpr};
diff --git a/tests-fuzz/src/ir/alter_expr.rs b/tests-fuzz/src/ir/alter_expr.rs
index a9fdc18c22..1d637ff660 100644
--- a/tests-fuzz/src/ir/alter_expr.rs
+++ b/tests-fuzz/src/ir/alter_expr.rs
@@ -12,16 +12,28 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+use std::fmt::Display;
+use std::str::FromStr;
+
+use common_base::readable_size::ReadableSize;
use common_query::AddColumnLocation;
+use common_time::{Duration, FOREVER, INSTANT};
use derive_builder::Builder;
use serde::{Deserialize, Serialize};
+use store_api::mito_engine_options::{
+ APPEND_MODE_KEY, COMPACTION_TYPE, TTL_KEY, TWCS_MAX_ACTIVE_WINDOW_FILES,
+ TWCS_MAX_ACTIVE_WINDOW_RUNS, TWCS_MAX_INACTIVE_WINDOW_FILES, TWCS_MAX_INACTIVE_WINDOW_RUNS,
+ TWCS_MAX_OUTPUT_FILE_SIZE, TWCS_TIME_WINDOW,
+};
+use strum::EnumIter;
+use crate::error::{self, Result};
use crate::ir::{Column, Ident};
#[derive(Debug, Builder, Clone, Serialize, Deserialize)]
pub struct AlterTableExpr {
pub table_name: Ident,
- pub alter_options: AlterTableOperation,
+ pub alter_kinds: AlterTableOperation,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -37,4 +49,196 @@ pub enum AlterTableOperation {
RenameTable { new_table_name: Ident },
/// `MODIFY COLUMN `
ModifyDataType { column: Column },
+ /// `SET = `
+ SetTableOptions { options: Vec },
+ /// `UNSET `
+ UnsetTableOptions { keys: Vec },
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
+pub enum Ttl {
+ Duration(Duration),
+ Instant,
+ #[default]
+ Forever,
+}
+
+impl Display for Ttl {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Ttl::Duration(d) => write!(f, "{}", d),
+ Ttl::Instant => write!(f, "{}", INSTANT),
+ Ttl::Forever => write!(f, "{}", FOREVER),
+ }
+ }
+}
+
+#[derive(Debug, EnumIter, Clone, Serialize, Deserialize, PartialEq, Eq)]
+pub enum AlterTableOption {
+ Ttl(Ttl),
+ TwcsTimeWindow(Duration),
+ TwcsMaxOutputFileSize(ReadableSize),
+ TwcsMaxInactiveWindowFiles(u64),
+ TwcsMaxActiveWindowFiles(u64),
+ TwcsMaxInactiveWindowRuns(u64),
+ TwcsMaxActiveWindowRuns(u64),
+}
+
+impl AlterTableOption {
+ pub fn key(&self) -> &str {
+ match self {
+ AlterTableOption::Ttl(_) => TTL_KEY,
+ AlterTableOption::TwcsTimeWindow(_) => TWCS_TIME_WINDOW,
+ AlterTableOption::TwcsMaxOutputFileSize(_) => TWCS_MAX_OUTPUT_FILE_SIZE,
+ AlterTableOption::TwcsMaxInactiveWindowFiles(_) => TWCS_MAX_INACTIVE_WINDOW_FILES,
+ AlterTableOption::TwcsMaxActiveWindowFiles(_) => TWCS_MAX_ACTIVE_WINDOW_FILES,
+ AlterTableOption::TwcsMaxInactiveWindowRuns(_) => TWCS_MAX_INACTIVE_WINDOW_RUNS,
+ AlterTableOption::TwcsMaxActiveWindowRuns(_) => TWCS_MAX_ACTIVE_WINDOW_RUNS,
+ }
+ }
+
+ /// Parses the AlterTableOption from a key-value pair
+ fn parse_kv(key: &str, value: &str) -> Result {
+ match key {
+ TTL_KEY => {
+ let ttl = if value.to_lowercase() == INSTANT {
+ Ttl::Instant
+ } else if value.to_lowercase() == FOREVER {
+ Ttl::Forever
+ } else {
+ let duration = humantime::parse_duration(value).unwrap();
+ Ttl::Duration(duration.into())
+ };
+ Ok(AlterTableOption::Ttl(ttl))
+ }
+ TWCS_MAX_ACTIVE_WINDOW_RUNS => {
+ let runs = value.parse().unwrap();
+ Ok(AlterTableOption::TwcsMaxActiveWindowRuns(runs))
+ }
+ TWCS_MAX_ACTIVE_WINDOW_FILES => {
+ let files = value.parse().unwrap();
+ Ok(AlterTableOption::TwcsMaxActiveWindowFiles(files))
+ }
+ TWCS_MAX_INACTIVE_WINDOW_RUNS => {
+ let runs = value.parse().unwrap();
+ Ok(AlterTableOption::TwcsMaxInactiveWindowRuns(runs))
+ }
+ TWCS_MAX_INACTIVE_WINDOW_FILES => {
+ let files = value.parse().unwrap();
+ Ok(AlterTableOption::TwcsMaxInactiveWindowFiles(files))
+ }
+ TWCS_MAX_OUTPUT_FILE_SIZE => {
+ // may be "1M" instead of "1 MiB"
+ let value = if value.ends_with("B") {
+ value.to_string()
+ } else {
+ format!("{}B", value)
+ };
+ let size = ReadableSize::from_str(&value).unwrap();
+ Ok(AlterTableOption::TwcsMaxOutputFileSize(size))
+ }
+ TWCS_TIME_WINDOW => {
+ let time = humantime::parse_duration(value).unwrap();
+ Ok(AlterTableOption::TwcsTimeWindow(time.into()))
+ }
+ _ => error::UnexpectedSnafu {
+ violated: format!("Unknown table option key: {}", key),
+ }
+ .fail(),
+ }
+ }
+
+ /// Parses the AlterTableOption from comma-separated string
+ pub fn parse_kv_pairs(option_string: &str) -> Result> {
+ let mut options = vec![];
+ for pair in option_string.split(',') {
+ let pair = pair.trim();
+ let (key, value) = pair.split_once('=').unwrap();
+ let key = key.trim().replace("\'", "");
+ let value = value.trim().replace('\'', "");
+ // Currently we have only one compaction type, so we ignore it
+ // Cautious: COMPACTION_TYPE may be kept even if there are no compaction options enabled
+ if key == COMPACTION_TYPE || key == APPEND_MODE_KEY {
+ continue;
+ } else {
+ let option = AlterTableOption::parse_kv(&key, &value)?;
+ options.push(option);
+ }
+ }
+ Ok(options)
+ }
+}
+
+impl Display for AlterTableOption {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ AlterTableOption::Ttl(d) => write!(f, "'{}' = '{}'", TTL_KEY, d),
+ AlterTableOption::TwcsTimeWindow(d) => write!(f, "'{}' = '{}'", TWCS_TIME_WINDOW, d),
+ AlterTableOption::TwcsMaxOutputFileSize(s) => {
+ // Caution: to_string loses precision for ReadableSize
+ write!(f, "'{}' = '{}'", TWCS_MAX_OUTPUT_FILE_SIZE, s)
+ }
+ AlterTableOption::TwcsMaxInactiveWindowFiles(u) => {
+ write!(f, "'{}' = '{}'", TWCS_MAX_INACTIVE_WINDOW_FILES, u)
+ }
+ AlterTableOption::TwcsMaxActiveWindowFiles(u) => {
+ write!(f, "'{}' = '{}'", TWCS_MAX_ACTIVE_WINDOW_FILES, u)
+ }
+ AlterTableOption::TwcsMaxInactiveWindowRuns(u) => {
+ write!(f, "'{}' = '{}'", TWCS_MAX_INACTIVE_WINDOW_RUNS, u)
+ }
+ AlterTableOption::TwcsMaxActiveWindowRuns(u) => {
+ write!(f, "'{}' = '{}'", TWCS_MAX_ACTIVE_WINDOW_RUNS, u)
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_parse_kv_pairs() {
+ let option_string =
+ "compaction.twcs.max_output_file_size = '1M', compaction.type = 'twcs', ttl = 'forever'";
+ let options = AlterTableOption::parse_kv_pairs(option_string).unwrap();
+ assert_eq!(options.len(), 2);
+ assert_eq!(
+ options,
+ vec![
+ AlterTableOption::TwcsMaxOutputFileSize(ReadableSize::from_str("1MB").unwrap()),
+ AlterTableOption::Ttl(Ttl::Forever),
+ ]
+ );
+
+ let option_string = "compaction.twcs.max_active_window_files = '5030469694939972912',
+ compaction.twcs.max_active_window_runs = '8361168990283879099',
+ compaction.twcs.max_inactive_window_files = '6028716566907830876',
+ compaction.twcs.max_inactive_window_runs = '10622283085591494074',
+ compaction.twcs.max_output_file_size = '15686.4PiB',
+ compaction.twcs.time_window = '2061999256ms',
+ compaction.type = 'twcs',
+ ttl = '1month 3days 15h 49m 8s 279ms'";
+ let options = AlterTableOption::parse_kv_pairs(option_string).unwrap();
+ assert_eq!(options.len(), 7);
+ let expected = vec![
+ AlterTableOption::TwcsMaxActiveWindowFiles(5030469694939972912),
+ AlterTableOption::TwcsMaxActiveWindowRuns(8361168990283879099),
+ AlterTableOption::TwcsMaxInactiveWindowFiles(6028716566907830876),
+ AlterTableOption::TwcsMaxInactiveWindowRuns(10622283085591494074),
+ AlterTableOption::TwcsMaxOutputFileSize(ReadableSize::from_str("15686.4PiB").unwrap()),
+ AlterTableOption::TwcsTimeWindow(Duration::new_nanosecond(2_061_999_256_000_000)),
+ AlterTableOption::Ttl(Ttl::Duration(Duration::new_millisecond(
+ // A month is 2_630_016 seconds
+ 2_630_016 * 1000
+ + 3 * 24 * 60 * 60 * 1000
+ + 15 * 60 * 60 * 1000
+ + 49 * 60 * 1000
+ + 8 * 1000
+ + 279,
+ ))),
+ ];
+ assert_eq!(options, expected);
+ }
}
diff --git a/tests-fuzz/src/test_utils.rs b/tests-fuzz/src/test_utils.rs
index e65548969a..bef96a1fd7 100644
--- a/tests-fuzz/src/test_utils.rs
+++ b/tests-fuzz/src/test_utils.rs
@@ -55,5 +55,6 @@ pub fn new_test_ctx() -> TableContext {
],
partition: None,
primary_keys: vec![],
+ table_options: vec![],
}
}
diff --git a/tests-fuzz/src/translator.rs b/tests-fuzz/src/translator.rs
index 1745aa9336..673b543f2c 100644
--- a/tests-fuzz/src/translator.rs
+++ b/tests-fuzz/src/translator.rs
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+mod common;
pub mod mysql;
pub mod postgres;
diff --git a/tests-fuzz/src/translator/common.rs b/tests-fuzz/src/translator/common.rs
new file mode 100644
index 0000000000..2b968ed439
--- /dev/null
+++ b/tests-fuzz/src/translator/common.rs
@@ -0,0 +1,67 @@
+// Copyright 2023 Greptime Team
+//
+// 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::fmt::Display;
+
+use super::DslTranslator;
+use crate::error::{Error, Result};
+use crate::ir::alter_expr::AlterTableOperation;
+use crate::ir::{AlterTableExpr, AlterTableOption};
+
+/// Shared translator for `ALTER TABLE` operations.
+pub(crate) struct CommonAlterTableTranslator;
+
+impl DslTranslator for CommonAlterTableTranslator {
+ type Error = Error;
+
+ fn translate(&self, input: &AlterTableExpr) -> Result {
+ Ok(match &input.alter_kinds {
+ AlterTableOperation::DropColumn { name } => Self::format_drop(&input.table_name, name),
+ AlterTableOperation::SetTableOptions { options } => {
+ Self::format_set_table_options(&input.table_name, options)
+ }
+ AlterTableOperation::UnsetTableOptions { keys } => {
+ Self::format_unset_table_options(&input.table_name, keys)
+ }
+ _ => unimplemented!(),
+ })
+ }
+}
+
+impl CommonAlterTableTranslator {
+ fn format_drop(name: impl Display, column: impl Display) -> String {
+ format!("ALTER TABLE {name} DROP COLUMN {column};")
+ }
+
+ fn format_set_table_options(name: impl Display, options: &[AlterTableOption]) -> String {
+ format!(
+ "ALTER TABLE {name} SET {};",
+ options
+ .iter()
+ .map(|option| option.to_string())
+ .collect::>()
+ .join(", ")
+ )
+ }
+
+ fn format_unset_table_options(name: impl Display, keys: &[String]) -> String {
+ format!(
+ "ALTER TABLE {name} UNSET {};",
+ keys.iter()
+ .map(|key| format!("'{}'", key))
+ .collect::>()
+ .join(", ")
+ )
+ }
+}
diff --git a/tests-fuzz/src/translator/mysql/alter_expr.rs b/tests-fuzz/src/translator/mysql/alter_expr.rs
index c973d7cb4b..3bf30b09a3 100644
--- a/tests-fuzz/src/translator/mysql/alter_expr.rs
+++ b/tests-fuzz/src/translator/mysql/alter_expr.rs
@@ -22,6 +22,7 @@ use crate::error::{Error, Result};
use crate::ir::alter_expr::AlterTableOperation;
use crate::ir::create_expr::ColumnOption;
use crate::ir::{AlterTableExpr, Column};
+use crate::translator::common::CommonAlterTableTranslator;
use crate::translator::DslTranslator;
pub struct AlterTableExprTranslator;
@@ -30,26 +31,22 @@ impl DslTranslator for AlterTableExprTranslator {
type Error = Error;
fn translate(&self, input: &AlterTableExpr) -> Result {
- Ok(match &input.alter_options {
+ Ok(match &input.alter_kinds {
AlterTableOperation::AddColumn { column, location } => {
Self::format_add_column(&input.table_name, column, location)
}
- AlterTableOperation::DropColumn { name } => Self::format_drop(&input.table_name, name),
AlterTableOperation::RenameTable { new_table_name } => {
Self::format_rename(&input.table_name, new_table_name)
}
AlterTableOperation::ModifyDataType { column } => {
Self::format_modify_data_type(&input.table_name, column)
}
+ _ => CommonAlterTableTranslator.translate(input)?,
})
}
}
impl AlterTableExprTranslator {
- fn format_drop(name: impl Display, column: impl Display) -> String {
- format!("ALTER TABLE {name} DROP COLUMN {column};")
- }
-
fn format_rename(name: impl Display, new_name: impl Display) -> String {
format!("ALTER TABLE {name} RENAME {new_name};")
}
@@ -119,11 +116,15 @@ impl AlterTableExprTranslator {
#[cfg(test)]
mod tests {
+ use std::str::FromStr;
+
+ use common_base::readable_size::ReadableSize;
use common_query::AddColumnLocation;
+ use common_time::Duration;
use datatypes::data_type::ConcreteDataType;
use super::AlterTableExprTranslator;
- use crate::ir::alter_expr::AlterTableOperation;
+ use crate::ir::alter_expr::{AlterTableOperation, AlterTableOption, Ttl};
use crate::ir::create_expr::ColumnOption;
use crate::ir::{AlterTableExpr, Column};
use crate::translator::DslTranslator;
@@ -132,7 +133,7 @@ mod tests {
fn test_alter_table_expr() {
let alter_expr = AlterTableExpr {
table_name: "test".into(),
- alter_options: AlterTableOperation::AddColumn {
+ alter_kinds: AlterTableOperation::AddColumn {
column: Column {
name: "host".into(),
column_type: ConcreteDataType::string_datatype(),
@@ -150,7 +151,7 @@ mod tests {
let alter_expr = AlterTableExpr {
table_name: "test".into(),
- alter_options: AlterTableOperation::RenameTable {
+ alter_kinds: AlterTableOperation::RenameTable {
new_table_name: "foo".into(),
},
};
@@ -160,7 +161,7 @@ mod tests {
let alter_expr = AlterTableExpr {
table_name: "test".into(),
- alter_options: AlterTableOperation::DropColumn { name: "foo".into() },
+ alter_kinds: AlterTableOperation::DropColumn { name: "foo".into() },
};
let output = AlterTableExprTranslator.translate(&alter_expr).unwrap();
@@ -168,7 +169,7 @@ mod tests {
let alter_expr = AlterTableExpr {
table_name: "test".into(),
- alter_options: AlterTableOperation::ModifyDataType {
+ alter_kinds: AlterTableOperation::ModifyDataType {
column: Column {
name: "host".into(),
column_type: ConcreteDataType::string_datatype(),
@@ -180,4 +181,48 @@ mod tests {
let output = AlterTableExprTranslator.translate(&alter_expr).unwrap();
assert_eq!("ALTER TABLE test MODIFY COLUMN host STRING;", output);
}
+
+ #[test]
+ fn test_alter_table_expr_set_table_options() {
+ let alter_expr = AlterTableExpr {
+ table_name: "test".into(),
+ alter_kinds: AlterTableOperation::SetTableOptions {
+ options: vec![
+ AlterTableOption::Ttl(Ttl::Duration(Duration::new_second(60))),
+ AlterTableOption::TwcsTimeWindow(Duration::new_second(60)),
+ AlterTableOption::TwcsMaxOutputFileSize(ReadableSize::from_str("1GB").unwrap()),
+ AlterTableOption::TwcsMaxActiveWindowFiles(10),
+ AlterTableOption::TwcsMaxActiveWindowRuns(10),
+ AlterTableOption::TwcsMaxInactiveWindowFiles(5),
+ AlterTableOption::TwcsMaxInactiveWindowRuns(5),
+ ],
+ },
+ };
+
+ let output = AlterTableExprTranslator.translate(&alter_expr).unwrap();
+ let expected = concat!(
+ "ALTER TABLE test SET 'ttl' = '60s', ",
+ "'compaction.twcs.time_window' = '60s', ",
+ "'compaction.twcs.max_output_file_size' = '1.0GiB', ",
+ "'compaction.twcs.max_active_window_files' = '10', ",
+ "'compaction.twcs.max_active_window_runs' = '10', ",
+ "'compaction.twcs.max_inactive_window_files' = '5', ",
+ "'compaction.twcs.max_inactive_window_runs' = '5';"
+ );
+ assert_eq!(expected, output);
+ }
+
+ #[test]
+ fn test_alter_table_expr_unset_table_options() {
+ let alter_expr = AlterTableExpr {
+ table_name: "test".into(),
+ alter_kinds: AlterTableOperation::UnsetTableOptions {
+ keys: vec!["ttl".into(), "compaction.twcs.time_window".into()],
+ },
+ };
+
+ let output = AlterTableExprTranslator.translate(&alter_expr).unwrap();
+ let expected = "ALTER TABLE test UNSET 'ttl', 'compaction.twcs.time_window';";
+ assert_eq!(expected, output);
+ }
}
diff --git a/tests-fuzz/src/translator/postgres/alter_expr.rs b/tests-fuzz/src/translator/postgres/alter_expr.rs
index 42db202efe..f66ce0db92 100644
--- a/tests-fuzz/src/translator/postgres/alter_expr.rs
+++ b/tests-fuzz/src/translator/postgres/alter_expr.rs
@@ -21,6 +21,7 @@ use crate::error::{Error, Result};
use crate::ir::alter_expr::AlterTableOperation;
use crate::ir::create_expr::ColumnOption;
use crate::ir::{AlterTableExpr, Column};
+use crate::translator::common::CommonAlterTableTranslator;
use crate::translator::postgres::sql_data_type_to_postgres_data_type;
use crate::translator::DslTranslator;
@@ -30,26 +31,22 @@ impl DslTranslator for AlterTableExprTranslator {
type Error = Error;
fn translate(&self, input: &AlterTableExpr) -> Result {
- Ok(match &input.alter_options {
+ Ok(match &input.alter_kinds {
AlterTableOperation::AddColumn { column, .. } => {
Self::format_add_column(&input.table_name, column)
}
- AlterTableOperation::DropColumn { name } => Self::format_drop(&input.table_name, name),
AlterTableOperation::RenameTable { new_table_name } => {
Self::format_rename(&input.table_name, new_table_name)
}
AlterTableOperation::ModifyDataType { column } => {
Self::format_modify_data_type(&input.table_name, column)
}
+ _ => CommonAlterTableTranslator.translate(input)?,
})
}
}
impl AlterTableExprTranslator {
- fn format_drop(name: impl Display, column: impl Display) -> String {
- format!("ALTER TABLE {name} DROP COLUMN {column};")
- }
-
fn format_rename(name: impl Display, new_name: impl Display) -> String {
format!("ALTER TABLE {name} RENAME TO {new_name};")
}
@@ -116,11 +113,15 @@ impl AlterTableExprTranslator {
#[cfg(test)]
mod tests {
+ use std::str::FromStr;
+
+ use common_base::readable_size::ReadableSize;
use common_query::AddColumnLocation;
+ use common_time::Duration;
use datatypes::data_type::ConcreteDataType;
use super::AlterTableExprTranslator;
- use crate::ir::alter_expr::AlterTableOperation;
+ use crate::ir::alter_expr::{AlterTableOperation, AlterTableOption, Ttl};
use crate::ir::create_expr::ColumnOption;
use crate::ir::{AlterTableExpr, Column};
use crate::translator::DslTranslator;
@@ -129,7 +130,7 @@ mod tests {
fn test_alter_table_expr() {
let alter_expr = AlterTableExpr {
table_name: "test".into(),
- alter_options: AlterTableOperation::AddColumn {
+ alter_kinds: AlterTableOperation::AddColumn {
column: Column {
name: "host".into(),
column_type: ConcreteDataType::string_datatype(),
@@ -145,7 +146,7 @@ mod tests {
let alter_expr = AlterTableExpr {
table_name: "test".into(),
- alter_options: AlterTableOperation::RenameTable {
+ alter_kinds: AlterTableOperation::RenameTable {
new_table_name: "foo".into(),
},
};
@@ -155,7 +156,7 @@ mod tests {
let alter_expr = AlterTableExpr {
table_name: "test".into(),
- alter_options: AlterTableOperation::DropColumn { name: "foo".into() },
+ alter_kinds: AlterTableOperation::DropColumn { name: "foo".into() },
};
let output = AlterTableExprTranslator.translate(&alter_expr).unwrap();
@@ -163,7 +164,7 @@ mod tests {
let alter_expr = AlterTableExpr {
table_name: "test".into(),
- alter_options: AlterTableOperation::ModifyDataType {
+ alter_kinds: AlterTableOperation::ModifyDataType {
column: Column {
name: "host".into(),
column_type: ConcreteDataType::string_datatype(),
@@ -176,4 +177,48 @@ mod tests {
// Ignores the location and primary key option.
assert_eq!("ALTER TABLE test MODIFY COLUMN host STRING;", output);
}
+
+ #[test]
+ fn test_alter_table_expr_set_table_options() {
+ let alter_expr = AlterTableExpr {
+ table_name: "test".into(),
+ alter_kinds: AlterTableOperation::SetTableOptions {
+ options: vec![
+ AlterTableOption::Ttl(Ttl::Duration(Duration::new_second(60))),
+ AlterTableOption::TwcsTimeWindow(Duration::new_second(60)),
+ AlterTableOption::TwcsMaxOutputFileSize(ReadableSize::from_str("1GB").unwrap()),
+ AlterTableOption::TwcsMaxActiveWindowFiles(10),
+ AlterTableOption::TwcsMaxActiveWindowRuns(10),
+ AlterTableOption::TwcsMaxInactiveWindowFiles(5),
+ AlterTableOption::TwcsMaxInactiveWindowRuns(5),
+ ],
+ },
+ };
+
+ let output = AlterTableExprTranslator.translate(&alter_expr).unwrap();
+ let expected = concat!(
+ "ALTER TABLE test SET 'ttl' = '60s', ",
+ "'compaction.twcs.time_window' = '60s', ",
+ "'compaction.twcs.max_output_file_size' = '1.0GiB', ",
+ "'compaction.twcs.max_active_window_files' = '10', ",
+ "'compaction.twcs.max_active_window_runs' = '10', ",
+ "'compaction.twcs.max_inactive_window_files' = '5', ",
+ "'compaction.twcs.max_inactive_window_runs' = '5';"
+ );
+ assert_eq!(expected, output);
+ }
+
+ #[test]
+ fn test_alter_table_expr_unset_table_options() {
+ let alter_expr = AlterTableExpr {
+ table_name: "test".into(),
+ alter_kinds: AlterTableOperation::UnsetTableOptions {
+ keys: vec!["ttl".into(), "compaction.twcs.time_window".into()],
+ },
+ };
+
+ let output = AlterTableExprTranslator.translate(&alter_expr).unwrap();
+ let expected = "ALTER TABLE test UNSET 'ttl', 'compaction.twcs.time_window';";
+ assert_eq!(expected, output);
+ }
}
diff --git a/tests-fuzz/src/validator.rs b/tests-fuzz/src/validator.rs
index cf2df9af22..406dd66041 100644
--- a/tests-fuzz/src/validator.rs
+++ b/tests-fuzz/src/validator.rs
@@ -14,3 +14,4 @@
pub mod column;
pub mod row;
+pub mod table;
diff --git a/tests-fuzz/src/validator/table.rs b/tests-fuzz/src/validator/table.rs
new file mode 100644
index 0000000000..406719b2d6
--- /dev/null
+++ b/tests-fuzz/src/validator/table.rs
@@ -0,0 +1,103 @@
+// Copyright 2023 Greptime Team
+//
+// 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 snafu::{ensure, ResultExt};
+use sqlx::database::HasArguments;
+use sqlx::{ColumnIndex, Database, Decode, Encode, Executor, IntoArguments, Row, Type};
+
+use crate::error::{self, Result, UnexpectedSnafu};
+use crate::ir::alter_expr::AlterTableOption;
+
+/// Parses table options from the result of `SHOW CREATE TABLE`
+/// An example of the result of `SHOW CREATE TABLE`:
+/// +-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+/// | Table | Create Table |
+/// +-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+/// | json | CREATE TABLE IF NOT EXISTS `json` (`ts` TIMESTAMP(3) NOT NULL, `j` JSON NULL, TIME INDEX (`ts`)) ENGINE=mito WITH(compaction.twcs.max_output_file_size = '1M', compaction.type = 'twcs', ttl = '1day') |
+/// +-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+fn parse_show_create(show_create: &str) -> Result> {
+ if let Some(option_start) = show_create.find("WITH(") {
+ let option_end = {
+ let remain_str = &show_create[option_start..];
+ if let Some(end) = remain_str.find(')') {
+ end + option_start
+ } else {
+ return UnexpectedSnafu {
+ violated: format!("Cannot find the end of the options in: {}", show_create),
+ }
+ .fail();
+ }
+ };
+ let options = &show_create[option_start + 5..option_end];
+ Ok(AlterTableOption::parse_kv_pairs(options)?)
+ } else {
+ Ok(vec![])
+ }
+}
+
+/// Fetches table options from the context
+pub async fn fetch_table_options<'a, DB, E>(e: E, sql: &'a str) -> Result>
+where
+ DB: Database,
+ >::Arguments: IntoArguments<'a, DB>,
+ for<'c> E: 'a + Executor<'c, Database = DB>,
+ for<'c> String: Decode<'c, DB> + Type,
+ for<'c> String: Encode<'c, DB> + Type,
+ usize: ColumnIndex<::Row>,
+{
+ let fetched_rows = sqlx::query(sql)
+ .fetch_all(e)
+ .await
+ .context(error::ExecuteQuerySnafu { sql })?;
+ ensure!(
+ fetched_rows.len() == 1,
+ error::AssertSnafu {
+ reason: format!(
+ "Expected fetched row length: 1, got: {}",
+ fetched_rows.len(),
+ )
+ }
+ );
+
+ let row = fetched_rows.first().unwrap();
+ let show_create = row.try_get::(1).unwrap();
+ parse_show_create(&show_create)
+}
+
+#[cfg(test)]
+mod tests {
+ use std::str::FromStr;
+
+ use common_base::readable_size::ReadableSize;
+ use common_time::Duration;
+
+ use super::*;
+ use crate::ir::alter_expr::Ttl;
+ use crate::ir::AlterTableOption;
+
+ #[test]
+ fn test_parse_show_create() {
+ let show_create = "CREATE TABLE IF NOT EXISTS `json` (`ts` TIMESTAMP(3) NOT NULL, `j` JSON NULL, TIME INDEX (`ts`)) ENGINE=mito WITH(compaction.twcs.max_output_file_size = '1M', compaction.type = 'twcs', ttl = '1day')";
+ let options = parse_show_create(show_create).unwrap();
+ assert_eq!(options.len(), 2);
+ assert_eq!(
+ options[0],
+ AlterTableOption::TwcsMaxOutputFileSize(ReadableSize::from_str("1MB").unwrap())
+ );
+ assert_eq!(
+ options[1],
+ AlterTableOption::Ttl(Ttl::Duration(Duration::new_second(24 * 60 * 60)))
+ );
+ }
+}
diff --git a/tests-fuzz/targets/fuzz_alter_logical_table.rs b/tests-fuzz/targets/ddl/fuzz_alter_logical_table.rs
similarity index 100%
rename from tests-fuzz/targets/fuzz_alter_logical_table.rs
rename to tests-fuzz/targets/ddl/fuzz_alter_logical_table.rs
diff --git a/tests-fuzz/targets/fuzz_alter_table.rs b/tests-fuzz/targets/ddl/fuzz_alter_table.rs
similarity index 72%
rename from tests-fuzz/targets/fuzz_alter_table.rs
rename to tests-fuzz/targets/ddl/fuzz_alter_table.rs
index 7f2a809c9e..247d7632ee 100644
--- a/tests-fuzz/targets/fuzz_alter_table.rs
+++ b/tests-fuzz/targets/ddl/fuzz_alter_table.rs
@@ -34,10 +34,13 @@ use tests_fuzz::fake::{
use tests_fuzz::generator::alter_expr::{
AlterExprAddColumnGeneratorBuilder, AlterExprDropColumnGeneratorBuilder,
AlterExprModifyDataTypeGeneratorBuilder, AlterExprRenameGeneratorBuilder,
+ AlterExprSetTableOptionsGeneratorBuilder, AlterExprUnsetTableOptionsGeneratorBuilder,
};
use tests_fuzz::generator::create_expr::CreateTableExprGeneratorBuilder;
use tests_fuzz::generator::Generator;
-use tests_fuzz::ir::{droppable_columns, modifiable_columns, AlterTableExpr, CreateTableExpr};
+use tests_fuzz::ir::{
+ droppable_columns, modifiable_columns, AlterTableExpr, AlterTableOption, CreateTableExpr,
+};
use tests_fuzz::translator::mysql::alter_expr::AlterTableExprTranslator;
use tests_fuzz::translator::mysql::create_expr::CreateTableExprTranslator;
use tests_fuzz::translator::DslTranslator;
@@ -62,11 +65,13 @@ struct FuzzInput {
}
#[derive(Debug, EnumIter)]
-enum AlterTableOption {
+enum AlterTableKind {
AddColumn,
DropColumn,
RenameTable,
ModifyDataType,
+ SetTableOptions,
+ UnsetTableOptions,
}
fn generate_create_table_expr(rng: &mut R) -> Result {
@@ -93,23 +98,23 @@ fn generate_alter_table_expr(
table_ctx: TableContextRef,
rng: &mut R,
) -> Result {
- let options = AlterTableOption::iter().collect::>();
- match options[rng.gen_range(0..options.len())] {
- AlterTableOption::DropColumn if !droppable_columns(&table_ctx.columns).is_empty() => {
+ let kinds = AlterTableKind::iter().collect::>();
+ match kinds[rng.gen_range(0..kinds.len())] {
+ AlterTableKind::DropColumn if !droppable_columns(&table_ctx.columns).is_empty() => {
AlterExprDropColumnGeneratorBuilder::default()
.table_ctx(table_ctx)
.build()
.unwrap()
.generate(rng)
}
- AlterTableOption::ModifyDataType if !modifiable_columns(&table_ctx.columns).is_empty() => {
+ AlterTableKind::ModifyDataType if !modifiable_columns(&table_ctx.columns).is_empty() => {
AlterExprModifyDataTypeGeneratorBuilder::default()
.table_ctx(table_ctx)
.build()
.unwrap()
.generate(rng)
}
- AlterTableOption::RenameTable => AlterExprRenameGeneratorBuilder::default()
+ AlterTableKind::RenameTable => AlterExprRenameGeneratorBuilder::default()
.table_ctx(table_ctx)
.name_generator(Box::new(MappedGenerator::new(
WordGenerator,
@@ -118,6 +123,20 @@ fn generate_alter_table_expr(
.build()
.unwrap()
.generate(rng),
+ AlterTableKind::SetTableOptions => {
+ let expr_generator = AlterExprSetTableOptionsGeneratorBuilder::default()
+ .table_ctx(table_ctx)
+ .build()
+ .unwrap();
+ expr_generator.generate(rng)
+ }
+ AlterTableKind::UnsetTableOptions => {
+ let expr_generator = AlterExprUnsetTableOptionsGeneratorBuilder::default()
+ .table_ctx(table_ctx)
+ .build()
+ .unwrap();
+ expr_generator.generate(rng)
+ }
_ => {
let location = rng.gen_bool(0.5);
let expr_generator = AlterExprAddColumnGeneratorBuilder::default()
@@ -179,6 +198,31 @@ async fn execute_alter_table(ctx: FuzzContext, input: FuzzInput) -> Result<()> {
let mut columns = table_ctx.columns.clone();
columns.sort_by(|a, b| a.name.value.cmp(&b.name.value));
validator::column::assert_eq(&column_entries, &columns)?;
+
+ // Validates table options
+ let sql = format!("SHOW CREATE TABLE {}", table_ctx.name);
+ let mut table_options = validator::table::fetch_table_options(&ctx.greptime, &sql).await?;
+ table_options.sort_by(|a, b| a.key().cmp(b.key()));
+ let mut expected_table_options = table_ctx.table_options.clone();
+ expected_table_options.sort_by(|a, b| a.key().cmp(b.key()));
+ table_options
+ .iter()
+ .zip(expected_table_options.iter())
+ .for_each(|(a, b)| {
+ if let (
+ AlterTableOption::TwcsMaxOutputFileSize(a),
+ AlterTableOption::TwcsMaxOutputFileSize(b),
+ ) = (a, b)
+ {
+ // to_string loses precision for ReadableSize, so the size in generated SQL is not the same as the size in the table context,
+ // but the string representation should be the same. For example:
+ // to_string() from_str()
+ // ReadableSize(13001360408898724524) ------------> "11547.5PiB" -----------> ReadableSize(13001329174265200640)
+ assert_eq!(a.to_string(), b.to_string());
+ } else {
+ assert_eq!(a, b);
+ }
+ });
}
// Cleans up
diff --git a/tests-fuzz/targets/fuzz_create_database.rs b/tests-fuzz/targets/ddl/fuzz_create_database.rs
similarity index 100%
rename from tests-fuzz/targets/fuzz_create_database.rs
rename to tests-fuzz/targets/ddl/fuzz_create_database.rs
diff --git a/tests-fuzz/targets/fuzz_create_logical_table.rs b/tests-fuzz/targets/ddl/fuzz_create_logical_table.rs
similarity index 100%
rename from tests-fuzz/targets/fuzz_create_logical_table.rs
rename to tests-fuzz/targets/ddl/fuzz_create_logical_table.rs
diff --git a/tests-fuzz/targets/fuzz_create_table.rs b/tests-fuzz/targets/ddl/fuzz_create_table.rs
similarity index 100%
rename from tests-fuzz/targets/fuzz_create_table.rs
rename to tests-fuzz/targets/ddl/fuzz_create_table.rs