tests_fuzz/generator/
alter_expr.rs

1// Copyright 2023 Greptime Team
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
15use 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    // 0 -> NULL
41    // 1 -> DEFAULT VALUE
42    // 2 -> PRIMARY KEY + DEFAULT VALUE
43    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/// Generates the [AlterTableOperation::AddColumn] of [AlterTableExpr].
64#[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/// Generates the [AlterTableOperation::DropColumn] of [AlterTableExpr].
118#[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/// Generates the [AlterTableOperation::RenameTable] of [AlterTableExpr].
141#[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/// Generates the [AlterTableOperation::ModifyDataType] of [AlterTableExpr].
164#[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/// Generates the [AlterTableOperation::SetTableOptions] of [AlterTableExpr].
197#[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        // Generate random distinct options
211        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/// Generates the [AlterTableOperation::UnsetTableOptions] of [AlterTableExpr].
255#[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        // Generate random distinct options
269        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}