1use std::marker::PhantomData;
16
17use common_base::readable_size::ReadableSize;
18use common_query::AddColumnLocation;
19use datatypes::data_type::ConcreteDataType;
20use derive_builder::Builder;
21use rand::Rng;
22use snafu::ensure;
23use strum::IntoEnumIterator;
24
25use crate::context::TableContextRef;
26use crate::error::{self, Error, Result};
27use crate::fake::WordGenerator;
28use crate::generator::{ColumnOptionGenerator, ConcreteDataTypeGenerator, Generator, Random};
29use crate::ir::alter_expr::{AlterTableExpr, AlterTableOperation, AlterTableOption, Ttl};
30use crate::ir::create_expr::ColumnOption;
31use crate::ir::{
32 Column, ColumnTypeGenerator, Ident, droppable_columns, generate_columns, generate_random_value,
33 modifiable_columns,
34};
35
36fn add_column_options_generator<R: Rng>(
37 rng: &mut R,
38 column_type: &ConcreteDataType,
39) -> Vec<ColumnOption> {
40 let idx = rng.random_range(0..3);
44 match idx {
45 0 => vec![ColumnOption::Null],
46 1 => {
47 vec![ColumnOption::DefaultValue(generate_random_value(
48 rng,
49 column_type,
50 None,
51 ))]
52 }
53 2 => {
54 vec![
55 ColumnOption::PrimaryKey,
56 ColumnOption::DefaultValue(generate_random_value(rng, column_type, None)),
57 ]
58 }
59 _ => unreachable!(),
60 }
61}
62
63#[derive(Builder)]
65#[builder(pattern = "owned")]
66pub struct AlterExprAddColumnGenerator<R: Rng + 'static> {
67 table_ctx: TableContextRef,
68 #[builder(default)]
69 location: bool,
70 #[builder(default = "Box::new(WordGenerator)")]
71 name_generator: Box<dyn Random<Ident, R>>,
72 #[builder(default = "Box::new(add_column_options_generator)")]
73 column_options_generator: ColumnOptionGenerator<R>,
74 #[builder(default = "Box::new(ColumnTypeGenerator)")]
75 column_type_generator: ConcreteDataTypeGenerator<R>,
76}
77
78impl<R: Rng + 'static> Generator<AlterTableExpr, R> for AlterExprAddColumnGenerator<R> {
79 type Error = Error;
80
81 fn generate(&self, rng: &mut R) -> Result<AlterTableExpr> {
82 let with_location = self.location && rng.random::<bool>();
83 let location = if with_location {
84 let use_first = rng.random::<bool>();
85 let location = if use_first {
86 AddColumnLocation::First
87 } else {
88 AddColumnLocation::After {
89 column_name: self.table_ctx.columns
90 [rng.random_range(0..self.table_ctx.columns.len())]
91 .name
92 .to_string(),
93 }
94 };
95 Some(location)
96 } else {
97 None
98 };
99
100 let name = self
101 .table_ctx
102 .generate_unique_column_name(rng, self.name_generator.as_ref());
103 let column = generate_columns(
104 rng,
105 vec![name],
106 self.column_type_generator.as_ref(),
107 self.column_options_generator.as_ref(),
108 )
109 .remove(0);
110 Ok(AlterTableExpr {
111 table_name: self.table_ctx.name.clone(),
112 alter_kinds: AlterTableOperation::AddColumn { column, location },
113 })
114 }
115}
116
117#[derive(Builder)]
119#[builder(pattern = "owned")]
120pub struct AlterExprDropColumnGenerator<R> {
121 table_ctx: TableContextRef,
122 #[builder(default)]
123 _phantom: PhantomData<R>,
124}
125
126impl<R: Rng> Generator<AlterTableExpr, R> for AlterExprDropColumnGenerator<R> {
127 type Error = Error;
128
129 fn generate(&self, rng: &mut R) -> Result<AlterTableExpr> {
130 let droppable = droppable_columns(&self.table_ctx.columns);
131 ensure!(!droppable.is_empty(), error::DroppableColumnsSnafu);
132 let name = droppable[rng.random_range(0..droppable.len())].name.clone();
133 Ok(AlterTableExpr {
134 table_name: self.table_ctx.name.clone(),
135 alter_kinds: AlterTableOperation::DropColumn { name },
136 })
137 }
138}
139
140#[derive(Builder)]
142#[builder(pattern = "owned")]
143pub struct AlterExprRenameGenerator<R: Rng> {
144 table_ctx: TableContextRef,
145 #[builder(default = "Box::new(WordGenerator)")]
146 name_generator: Box<dyn Random<Ident, R>>,
147}
148
149impl<R: Rng> Generator<AlterTableExpr, R> for AlterExprRenameGenerator<R> {
150 type Error = Error;
151
152 fn generate(&self, rng: &mut R) -> Result<AlterTableExpr> {
153 let new_table_name = self
154 .table_ctx
155 .generate_unique_table_name(rng, self.name_generator.as_ref());
156 Ok(AlterTableExpr {
157 table_name: self.table_ctx.name.clone(),
158 alter_kinds: AlterTableOperation::RenameTable { new_table_name },
159 })
160 }
161}
162
163#[derive(Builder)]
165#[builder(pattern = "owned")]
166pub struct AlterExprModifyDataTypeGenerator<R: Rng> {
167 table_ctx: TableContextRef,
168 #[builder(default = "Box::new(ColumnTypeGenerator)")]
169 column_type_generator: ConcreteDataTypeGenerator<R>,
170}
171
172impl<R: Rng> Generator<AlterTableExpr, R> for AlterExprModifyDataTypeGenerator<R> {
173 type Error = Error;
174
175 fn generate(&self, rng: &mut R) -> Result<AlterTableExpr> {
176 let modifiable = modifiable_columns(&self.table_ctx.columns);
177 let changed = modifiable[rng.random_range(0..modifiable.len())].clone();
178 let mut to_type = self.column_type_generator.generate(rng);
179 while !changed.column_type.can_arrow_type_cast_to(&to_type) {
180 to_type = self.column_type_generator.generate(rng);
181 }
182
183 Ok(AlterTableExpr {
184 table_name: self.table_ctx.name.clone(),
185 alter_kinds: AlterTableOperation::ModifyDataType {
186 column: Column {
187 name: changed.name,
188 column_type: to_type,
189 options: vec![],
190 },
191 },
192 })
193 }
194}
195
196#[derive(Builder)]
198#[builder(pattern = "owned")]
199pub struct AlterExprSetTableOptionsGenerator<R: Rng> {
200 table_ctx: TableContextRef,
201 #[builder(default)]
202 _phantom: PhantomData<R>,
203}
204
205impl<R: Rng> Generator<AlterTableExpr, R> for AlterExprSetTableOptionsGenerator<R> {
206 type Error = Error;
207
208 fn generate(&self, rng: &mut R) -> Result<AlterTableExpr> {
209 let all_options = AlterTableOption::iter().collect::<Vec<_>>();
210 let mut option_templates_idx = vec![];
212 for _ in 1..rng.random_range(2..=all_options.len()) {
213 let option = rng.random_range(0..all_options.len());
214 if !option_templates_idx.contains(&option) {
215 option_templates_idx.push(option);
216 }
217 }
218 let options = option_templates_idx
219 .iter()
220 .map(|idx| match all_options[*idx] {
221 AlterTableOption::Ttl(_) => {
222 AlterTableOption::Ttl(Ttl::Forever)
225 }
226 AlterTableOption::TwcsTimeWindow(_) => {
227 let time_window: u32 = rng.random();
228 AlterTableOption::TwcsTimeWindow((time_window as i64).into())
229 }
230 AlterTableOption::TwcsMaxOutputFileSize(_) => {
231 let max_output_file_size: u64 = rng.random();
232 AlterTableOption::TwcsMaxOutputFileSize(ReadableSize(max_output_file_size))
233 }
234 AlterTableOption::TwcsTriggerFileNum(_) => {
235 let trigger_file_num: u64 = rng.random();
236 AlterTableOption::TwcsTriggerFileNum(trigger_file_num)
237 }
238 })
239 .collect();
240 Ok(AlterTableExpr {
241 table_name: self.table_ctx.name.clone(),
242 alter_kinds: AlterTableOperation::SetTableOptions { options },
243 })
244 }
245}
246
247#[derive(Builder)]
249#[builder(pattern = "owned")]
250pub struct AlterExprUnsetTableOptionsGenerator<R: Rng> {
251 table_ctx: TableContextRef,
252 #[builder(default)]
253 _phantom: PhantomData<R>,
254}
255
256impl<R: Rng> Generator<AlterTableExpr, R> for AlterExprUnsetTableOptionsGenerator<R> {
257 type Error = Error;
258
259 fn generate(&self, rng: &mut R) -> Result<AlterTableExpr> {
260 let all_options = AlterTableOption::iter().collect::<Vec<_>>();
261 let mut option_templates_idx = vec![];
263 for _ in 1..rng.random_range(2..=all_options.len()) {
264 let option = rng.random_range(0..all_options.len());
265 if !option_templates_idx.contains(&option) {
266 option_templates_idx.push(option);
267 }
268 }
269 let options = option_templates_idx
270 .iter()
271 .map(|idx| all_options[*idx].key().to_string())
272 .collect();
273 Ok(AlterTableExpr {
274 table_name: self.table_ctx.name.clone(),
275 alter_kinds: AlterTableOperation::UnsetTableOptions { keys: options },
276 })
277 }
278}
279
280#[cfg(test)]
281mod tests {
282 use std::sync::Arc;
283
284 use rand::SeedableRng;
285
286 use super::*;
287 use crate::context::TableContext;
288 use crate::generator::Generator;
289 use crate::generator::create_expr::CreateTableExprGeneratorBuilder;
290
291 #[test]
292 fn test_alter_table_expr_generator_deterministic() {
293 let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(0);
294 let create_expr = CreateTableExprGeneratorBuilder::default()
295 .columns(10)
296 .build()
297 .unwrap()
298 .generate(&mut rng)
299 .unwrap();
300 let table_ctx = Arc::new(TableContext::from(&create_expr));
301
302 let expr = AlterExprAddColumnGeneratorBuilder::default()
303 .table_ctx(table_ctx.clone())
304 .build()
305 .unwrap()
306 .generate(&mut rng)
307 .unwrap();
308 let serialized = serde_json::to_string(&expr).unwrap();
309 let expected = r#"{"table_name":{"value":"quasi","quote_style":null},"alter_kinds":{"AddColumn":{"column":{"name":{"value":"consequatur","quote_style":null},"column_type":{"Float64":{}},"options":[{"DefaultValue":{"Float64":0.48809950435391647}}]},"location":null}}}"#;
310 assert_eq!(expected, serialized);
311
312 let expr = AlterExprRenameGeneratorBuilder::default()
313 .table_ctx(table_ctx.clone())
314 .build()
315 .unwrap()
316 .generate(&mut rng)
317 .unwrap();
318 let serialized = serde_json::to_string(&expr).unwrap();
319 let expected = r#"{"table_name":{"value":"quasi","quote_style":null},"alter_kinds":{"RenameTable":{"new_table_name":{"value":"voluptates","quote_style":null}}}}"#;
320 assert_eq!(expected, serialized);
321
322 let expr = AlterExprDropColumnGeneratorBuilder::default()
323 .table_ctx(table_ctx.clone())
324 .build()
325 .unwrap()
326 .generate(&mut rng)
327 .unwrap();
328 let serialized = serde_json::to_string(&expr).unwrap();
329 let expected = r#"{"table_name":{"value":"quasi","quote_style":null},"alter_kinds":{"DropColumn":{"name":{"value":"ImPEDiT","quote_style":null}}}}"#;
330 assert_eq!(expected, serialized);
331
332 let expr = AlterExprModifyDataTypeGeneratorBuilder::default()
333 .table_ctx(table_ctx.clone())
334 .build()
335 .unwrap()
336 .generate(&mut rng)
337 .unwrap();
338 let serialized = serde_json::to_string(&expr).unwrap();
339 let expected = r#"{"table_name":{"value":"quasi","quote_style":null},"alter_kinds":{"ModifyDataType":{"column":{"name":{"value":"ADIpisci","quote_style":null},"column_type":{"Int64":{}},"options":[]}}}}"#;
340 assert_eq!(expected, serialized);
341
342 let expr = AlterExprSetTableOptionsGeneratorBuilder::default()
343 .table_ctx(table_ctx.clone())
344 .build()
345 .unwrap()
346 .generate(&mut rng)
347 .unwrap();
348 let serialized = serde_json::to_string(&expr).unwrap();
349 let expected = r#"{"table_name":{"value":"quasi","quote_style":null},"alter_kinds":{"SetTableOptions":{"options":[{"TwcsTimeWindow":{"value":2428665013,"unit":"Millisecond"}}]}}}"#;
350 assert_eq!(expected, serialized);
351
352 let expr = AlterExprUnsetTableOptionsGeneratorBuilder::default()
353 .table_ctx(table_ctx)
354 .build()
355 .unwrap()
356 .generate(&mut rng)
357 .unwrap();
358 let serialized = serde_json::to_string(&expr).unwrap();
359 let expected = r#"{"table_name":{"value":"quasi","quote_style":null},"alter_kinds":{"UnsetTableOptions":{"keys":["compaction.twcs.trigger_file_num","compaction.twcs.time_window"]}}}"#;
360 assert_eq!(expected, serialized);
361 }
362}