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 droppable_columns, generate_columns, generate_random_value, modifiable_columns, Column,
33 ColumnTypeGenerator, Ident,
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.gen(rng);
179 while !changed.column_type.can_arrow_type_cast_to(&to_type) {
180 to_type = self.column_type_generator.gen(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 let ttl_type = rng.random_range(0..3);
223 match ttl_type {
224 0 => {
225 let duration: u32 = rng.random();
226 AlterTableOption::Ttl(Ttl::Duration((duration as i64).into()))
227 }
228 1 => AlterTableOption::Ttl(Ttl::Instant),
229 2 => AlterTableOption::Ttl(Ttl::Forever),
230 _ => unreachable!(),
231 }
232 }
233 AlterTableOption::TwcsTimeWindow(_) => {
234 let time_window: u32 = rng.random();
235 AlterTableOption::TwcsTimeWindow((time_window as i64).into())
236 }
237 AlterTableOption::TwcsMaxOutputFileSize(_) => {
238 let max_output_file_size: u64 = rng.random();
239 AlterTableOption::TwcsMaxOutputFileSize(ReadableSize(max_output_file_size))
240 }
241 AlterTableOption::TwcsTriggerFileNum(_) => {
242 let trigger_file_num: u64 = rng.random();
243 AlterTableOption::TwcsTriggerFileNum(trigger_file_num)
244 }
245 })
246 .collect();
247 Ok(AlterTableExpr {
248 table_name: self.table_ctx.name.clone(),
249 alter_kinds: AlterTableOperation::SetTableOptions { options },
250 })
251 }
252}
253
254#[derive(Builder)]
256#[builder(pattern = "owned")]
257pub struct AlterExprUnsetTableOptionsGenerator<R: Rng> {
258 table_ctx: TableContextRef,
259 #[builder(default)]
260 _phantom: PhantomData<R>,
261}
262
263impl<R: Rng> Generator<AlterTableExpr, R> for AlterExprUnsetTableOptionsGenerator<R> {
264 type Error = Error;
265
266 fn generate(&self, rng: &mut R) -> Result<AlterTableExpr> {
267 let all_options = AlterTableOption::iter().collect::<Vec<_>>();
268 let mut option_templates_idx = vec![];
270 for _ in 1..rng.random_range(2..=all_options.len()) {
271 let option = rng.random_range(0..all_options.len());
272 if !option_templates_idx.contains(&option) {
273 option_templates_idx.push(option);
274 }
275 }
276 let options = option_templates_idx
277 .iter()
278 .map(|idx| all_options[*idx].key().to_string())
279 .collect();
280 Ok(AlterTableExpr {
281 table_name: self.table_ctx.name.clone(),
282 alter_kinds: AlterTableOperation::UnsetTableOptions { keys: options },
283 })
284 }
285}
286
287#[cfg(test)]
288mod tests {
289 use std::sync::Arc;
290
291 use rand::SeedableRng;
292
293 use super::*;
294 use crate::context::TableContext;
295 use crate::generator::create_expr::CreateTableExprGeneratorBuilder;
296 use crate::generator::Generator;
297
298 #[test]
299 fn test_alter_table_expr_generator_deterministic() {
300 let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(0);
301 let create_expr = CreateTableExprGeneratorBuilder::default()
302 .columns(10)
303 .build()
304 .unwrap()
305 .generate(&mut rng)
306 .unwrap();
307 let table_ctx = Arc::new(TableContext::from(&create_expr));
308
309 let expr = AlterExprAddColumnGeneratorBuilder::default()
310 .table_ctx(table_ctx.clone())
311 .build()
312 .unwrap()
313 .generate(&mut rng)
314 .unwrap();
315 let serialized = serde_json::to_string(&expr).unwrap();
316 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}}}"#;
317 assert_eq!(expected, serialized);
318
319 let expr = AlterExprRenameGeneratorBuilder::default()
320 .table_ctx(table_ctx.clone())
321 .build()
322 .unwrap()
323 .generate(&mut rng)
324 .unwrap();
325 let serialized = serde_json::to_string(&expr).unwrap();
326 let expected = r#"{"table_name":{"value":"quasi","quote_style":null},"alter_kinds":{"RenameTable":{"new_table_name":{"value":"voluptates","quote_style":null}}}}"#;
327 assert_eq!(expected, serialized);
328
329 let expr = AlterExprDropColumnGeneratorBuilder::default()
330 .table_ctx(table_ctx.clone())
331 .build()
332 .unwrap()
333 .generate(&mut rng)
334 .unwrap();
335 let serialized = serde_json::to_string(&expr).unwrap();
336 let expected = r#"{"table_name":{"value":"quasi","quote_style":null},"alter_kinds":{"DropColumn":{"name":{"value":"ImPEDiT","quote_style":null}}}}"#;
337 assert_eq!(expected, serialized);
338
339 let expr = AlterExprModifyDataTypeGeneratorBuilder::default()
340 .table_ctx(table_ctx.clone())
341 .build()
342 .unwrap()
343 .generate(&mut rng)
344 .unwrap();
345 let serialized = serde_json::to_string(&expr).unwrap();
346 let expected = r#"{"table_name":{"value":"quasi","quote_style":null},"alter_kinds":{"ModifyDataType":{"column":{"name":{"value":"ADIpisci","quote_style":null},"column_type":{"Int64":{}},"options":[]}}}}"#;
347 assert_eq!(expected, serialized);
348
349 let expr = AlterExprSetTableOptionsGeneratorBuilder::default()
350 .table_ctx(table_ctx.clone())
351 .build()
352 .unwrap()
353 .generate(&mut rng)
354 .unwrap();
355 let serialized = serde_json::to_string(&expr).unwrap();
356 let expected = r#"{"table_name":{"value":"quasi","quote_style":null},"alter_kinds":{"SetTableOptions":{"options":[{"TwcsTimeWindow":{"value":2428665013,"unit":"Millisecond"}}]}}}"#;
357 assert_eq!(expected, serialized);
358
359 let expr = AlterExprUnsetTableOptionsGeneratorBuilder::default()
360 .table_ctx(table_ctx)
361 .build()
362 .unwrap()
363 .generate(&mut rng)
364 .unwrap();
365 let serialized = serde_json::to_string(&expr).unwrap();
366 let expected = r#"{"table_name":{"value":"quasi","quote_style":null},"alter_kinds":{"UnsetTableOptions":{"keys":["compaction.twcs.trigger_file_num","compaction.twcs.time_window"]}}}"#;
367 assert_eq!(expected, serialized);
368 }
369}