tests_fuzz/generator/
repartition_expr.rs1use derive_builder::Builder;
16use rand::Rng;
17
18use crate::context::TableContextRef;
19use crate::error::{Error, Result};
20use crate::generator::Generator;
21use crate::ir::partition_expr::{SimplePartitions, generate_unique_bound};
22use crate::ir::repartition_expr::{MergePartitionExpr, SplitPartitionExpr};
23
24#[derive(Builder)]
25#[builder(pattern = "owned")]
26pub struct SplitPartitionExprGenerator {
27 table_ctx: TableContextRef,
28}
29
30impl<R: Rng + 'static> Generator<SplitPartitionExpr, R> for SplitPartitionExprGenerator {
31 type Error = Error;
32
33 fn generate(&self, rng: &mut R) -> Result<SplitPartitionExpr> {
34 let table_name = self.table_ctx.name.clone();
35 let partition_def = self
36 .table_ctx
37 .partition
38 .as_ref()
39 .expect("expected partition def");
40 let mut partitions =
41 SimplePartitions::from_exprs(partition_def.columns[0].clone(), &partition_def.exprs)?;
42 let column = self
43 .table_ctx
44 .columns
45 .iter()
46 .find(|c| c.name.value == partitions.column_name.value)
47 .unwrap_or_else(|| {
48 panic!(
49 "partition column not found: {}, columns: {:?}",
50 partitions.column_name.value,
51 self.table_ctx
52 .columns
53 .iter()
54 .map(|c| &c.name.value)
55 .collect::<Vec<_>>()
56 )
57 });
58 let new_bound = generate_unique_bound(rng, &column.column_type, &partitions.bounds)?;
59 let insert_pos = partitions.insert_bound(new_bound)?;
60
61 let from_expr = partition_def.exprs[insert_pos].clone();
62 let new_exprs = partitions.generate()?;
63 let left = new_exprs[insert_pos].clone();
64 let right = new_exprs[insert_pos + 1].clone();
65 Ok(SplitPartitionExpr {
66 table_name,
67 target: from_expr,
68 into: vec![left, right],
69 })
70 }
71}
72
73#[derive(Builder)]
74#[builder(pattern = "owned")]
75pub struct MergePartitionExprGenerator {
76 table_ctx: TableContextRef,
77}
78
79impl<R: Rng + 'static> Generator<MergePartitionExpr, R> for MergePartitionExprGenerator {
80 type Error = Error;
81
82 fn generate(&self, rng: &mut R) -> Result<MergePartitionExpr> {
83 let table_name = self.table_ctx.name.clone();
84 let partition_def = self
85 .table_ctx
86 .partition
87 .as_ref()
88 .expect("expected partition def");
89 let mut partitions =
90 SimplePartitions::from_exprs(partition_def.columns[0].clone(), &partition_def.exprs)?;
91 let remove_idx = rng.random_range(0..partitions.bounds.len());
92 partitions.remove_bound(remove_idx)?;
93 let left = partition_def.exprs[remove_idx].clone();
94 let right = partition_def.exprs[remove_idx + 1].clone();
95
96 Ok(MergePartitionExpr {
97 table_name,
98 targets: vec![left, right],
99 })
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use std::sync::Arc;
106
107 use rand::{Rng, SeedableRng};
108
109 use crate::context::TableContext;
110 use crate::generator::Generator;
111 use crate::generator::create_expr::CreateTableExprGeneratorBuilder;
112 use crate::generator::repartition_expr::{
113 MergePartitionExprGeneratorBuilder, SplitPartitionExprGeneratorBuilder,
114 };
115 use crate::ir::repartition_expr::RepartitionExpr;
116 use crate::translator::DslTranslator;
117 use crate::translator::mysql::repartition_expr::RepartitionExprTranslator;
118
119 #[test]
120 fn test_split_partition_expr() {
121 common_telemetry::init_default_ut_logging();
122 let mut rng = rand::rng();
123 let seed = rng.random::<u64>();
124 common_telemetry::info!("initializing rng with seed: {seed}");
125 let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(seed);
126 let expr = CreateTableExprGeneratorBuilder::default()
127 .columns(10)
128 .partition(10)
129 .if_not_exists(true)
130 .engine("mito2")
131 .build()
132 .unwrap()
133 .generate(&mut rng)
134 .unwrap();
135 let table_ctx = Arc::new(TableContext::from(&expr));
136 SplitPartitionExprGeneratorBuilder::default()
137 .table_ctx(table_ctx)
138 .build()
139 .unwrap()
140 .generate(&mut rng)
141 .unwrap();
142 }
143
144 #[test]
145 fn test_split_partition_expr_deterministic() {
146 let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(1234);
147 let expr = CreateTableExprGeneratorBuilder::default()
148 .columns(10)
149 .partition(10)
150 .if_not_exists(true)
151 .engine("mito2")
152 .build()
153 .unwrap()
154 .generate(&mut rng)
155 .unwrap();
156 let table_ctx = Arc::new(TableContext::from(&expr));
157 let expr = SplitPartitionExprGeneratorBuilder::default()
158 .table_ctx(table_ctx)
159 .build()
160 .unwrap()
161 .generate(&mut rng)
162 .unwrap();
163
164 let sql = RepartitionExprTranslator
165 .translate(&RepartitionExpr::Split(expr))
166 .unwrap();
167 let expected = r#"ALTER TABLE quO SPLIT PARTITION (
168 MaGnI >= 1844674407370955160 AND MaGnI < 2767011611056432740
169) INTO (
170 MaGnI >= 1844674407370955160 AND MaGnI < 2290232243991136014,
171 MaGnI >= 2290232243991136014 AND MaGnI < 2767011611056432740
172);"#;
173 assert_eq!(expected, sql);
174 }
175
176 #[test]
177 fn test_merge_partition_expr() {
178 common_telemetry::init_default_ut_logging();
179 let mut rng = rand::rng();
180 let seed = rng.random::<u64>();
181 common_telemetry::info!("initializing rng with seed: {seed}");
182 let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(seed);
183 let expr = CreateTableExprGeneratorBuilder::default()
184 .columns(10)
185 .partition(10)
186 .if_not_exists(true)
187 .engine("mito2")
188 .build()
189 .unwrap()
190 .generate(&mut rng)
191 .unwrap();
192 let table_ctx = Arc::new(TableContext::from(&expr));
193 MergePartitionExprGeneratorBuilder::default()
194 .table_ctx(table_ctx)
195 .build()
196 .unwrap()
197 .generate(&mut rng)
198 .unwrap();
199 }
200
201 #[test]
202 fn test_merge_partition_expr_deterministic() {
203 let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(1234);
204 let expr = CreateTableExprGeneratorBuilder::default()
205 .columns(10)
206 .partition(10)
207 .if_not_exists(true)
208 .engine("mito2")
209 .build()
210 .unwrap()
211 .generate(&mut rng)
212 .unwrap();
213 let table_ctx = Arc::new(TableContext::from(&expr));
214 let expr = MergePartitionExprGeneratorBuilder::default()
215 .table_ctx(table_ctx)
216 .build()
217 .unwrap()
218 .generate(&mut rng)
219 .unwrap();
220
221 let sql = RepartitionExprTranslator
222 .translate(&RepartitionExpr::Merge(expr))
223 .unwrap();
224 let expected = r#"ALTER TABLE quO MERGE PARTITION (
225 MaGnI >= 3689348814741910320 AND MaGnI < 4611686018427387900,
226 MaGnI >= 4611686018427387900 AND MaGnI < 5534023222112865480
227);"#;
228 assert_eq!(expected, sql);
229 }
230}