1use std::collections::HashMap;
16
17use datatypes::data_type::ConcreteDataType;
18use datatypes::value::Value;
19use derive_builder::Builder;
20use rand::Rng;
21use rand::seq::SliceRandom;
22use snafu::{ResultExt, ensure};
23
24use super::Generator;
25use crate::context::TableContextRef;
26use crate::error::{self, Error, Result};
27use crate::fake::{MappedGenerator, WordGenerator, random_capitalize_map};
28use crate::generator::{ColumnOptionGenerator, ConcreteDataTypeGenerator, Random};
29use crate::ir::create_expr::{
30 ColumnOption, CreateDatabaseExprBuilder, CreateTableExprBuilder, PartitionDef,
31};
32use crate::ir::partition_expr::SimplePartitions;
33use crate::ir::{
34 Column, ColumnTypeGenerator, CreateDatabaseExpr, CreateTableExpr, Ident,
35 PartibleColumnTypeGenerator, StringColumnTypeGenerator, TsColumnTypeGenerator,
36 column_options_generator, generate_columns, generate_partition_bounds,
37 partible_column_options_generator, primary_key_options_generator, ts_column_options_generator,
38};
39
40#[derive(Builder)]
41#[builder(default, pattern = "owned")]
42pub struct CreateTableExprGenerator<R: Rng + 'static> {
43 columns: usize,
44 #[builder(setter(into))]
45 engine: String,
46 partition: usize,
47 partition_column: bool,
48 if_not_exists: bool,
49 #[builder(setter(into))]
50 name: Ident,
51 #[builder(setter(into))]
52 with_clause: HashMap<String, String>,
53 name_generator: Box<dyn Random<Ident, R>>,
54 ts_column_type_generator: ConcreteDataTypeGenerator<R>,
55 column_type_generator: ConcreteDataTypeGenerator<R>,
56 partible_column_type_generator: ConcreteDataTypeGenerator<R>,
57 partible_column_options_generator: ColumnOptionGenerator<R>,
58 column_options_generator: ColumnOptionGenerator<R>,
59 ts_column_options_generator: ColumnOptionGenerator<R>,
60}
61
62const DEFAULT_ENGINE: &str = "mito";
63
64impl<R: Rng + 'static> Default for CreateTableExprGenerator<R> {
65 fn default() -> Self {
66 Self {
67 columns: 0,
68 engine: DEFAULT_ENGINE.to_string(),
69 if_not_exists: false,
70 partition: 0,
71 partition_column: false,
72 name: Ident::new(""),
73 with_clause: HashMap::default(),
74 name_generator: Box::new(MappedGenerator::new(WordGenerator, random_capitalize_map)),
75 ts_column_type_generator: Box::new(TsColumnTypeGenerator),
76 column_type_generator: Box::new(ColumnTypeGenerator),
77 partible_column_type_generator: Box::new(PartibleColumnTypeGenerator),
78 partible_column_options_generator: Box::new(partible_column_options_generator),
79 column_options_generator: Box::new(column_options_generator),
80 ts_column_options_generator: Box::new(ts_column_options_generator),
81 }
82 }
83}
84
85impl<R: Rng + 'static> Generator<CreateTableExpr, R> for CreateTableExprGenerator<R> {
86 type Error = Error;
87
88 fn generate(&self, rng: &mut R) -> Result<CreateTableExpr> {
90 ensure!(
91 self.columns != 0,
92 error::UnexpectedSnafu {
93 violated: "The columns must larger than zero"
94 }
95 );
96
97 let mut builder = CreateTableExprBuilder::default();
98 let mut columns = Vec::with_capacity(self.columns);
99 let mut primary_keys = vec![];
100 let need_partible_column = self.partition > 1 || self.partition_column;
101 let mut column_names = self.name_generator.choose(rng, self.columns);
102
103 if self.columns == 1 {
104 let name = column_names.pop().unwrap();
107 let column = generate_columns(
108 rng,
109 vec![name.clone()],
110 self.ts_column_type_generator.as_ref(),
111 self.ts_column_options_generator.as_ref(),
112 )
113 .remove(0);
114 columns.push(column);
115 } else {
116 if need_partible_column {
118 let name = column_names.pop().unwrap();
120 let column = generate_columns(
121 rng,
122 vec![name.clone()],
123 self.partible_column_type_generator.as_ref(),
124 self.partible_column_options_generator.as_ref(),
125 )
126 .remove(0);
127
128 if self.partition > 1 {
129 let partition_def = generate_partition_def(
131 self.partition,
132 column.column_type.clone(),
133 name.clone(),
134 );
135 builder.partition(partition_def);
136 }
137 columns.push(column);
138 }
139 let name = column_names.pop().unwrap();
142 columns.extend(generate_columns(
143 rng,
144 vec![name],
145 self.ts_column_type_generator.as_ref(),
146 self.ts_column_options_generator.as_ref(),
147 ));
148 columns.extend(generate_columns(
150 rng,
151 column_names,
152 self.column_type_generator.as_ref(),
153 self.column_options_generator.as_ref(),
154 ));
155 }
156
157 for (idx, column) in columns.iter().enumerate() {
158 if column.is_primary_key() {
159 primary_keys.push(idx);
160 }
161 }
162 primary_keys.shuffle(rng);
164
165 builder.columns(columns);
166 builder.primary_keys(primary_keys);
167 builder.engine(self.engine.clone());
168 builder.if_not_exists(self.if_not_exists);
169 if self.name.is_empty() {
170 builder.table_name(self.name_generator.generate(rng));
171 } else {
172 builder.table_name(self.name.clone());
173 }
174 if !self.with_clause.is_empty() {
175 let mut options = HashMap::new();
176 for (key, value) in &self.with_clause {
177 options.insert(key.clone(), Value::from(value.clone()));
178 }
179 builder.options(options);
180 }
181 builder.build().context(error::BuildCreateTableExprSnafu)
182 }
183}
184
185pub fn generate_partition_def(
186 partitions: usize,
187 column_type: ConcreteDataType,
188 column_name: Ident,
189) -> PartitionDef {
190 assert!(partitions > 1, "partitions must be greater than 1");
191 let bounds = generate_partition_bounds(&column_type, partitions - 1);
192 let partitions = SimplePartitions::new(column_name.clone(), bounds);
193 let partition_exprs = partitions.generate().unwrap();
194
195 PartitionDef {
196 columns: vec![column_name.clone()],
197 exprs: partition_exprs,
198 }
199}
200
201fn metric_partition_column() -> Column {
202 Column {
203 name: Ident::new("host"),
204 column_type: ConcreteDataType::string_datatype(),
205 options: vec![ColumnOption::PrimaryKey],
206 }
207}
208
209pub fn generate_metric_partition_def(partitions: usize) -> PartitionDef {
210 assert!(partitions > 1, "partitions must be greater than 1");
211 let partition_column = metric_partition_column();
212 let bounds = generate_partition_bounds(&partition_column.column_type, partitions - 1);
213 let partitions = SimplePartitions::new(partition_column.name.clone(), bounds);
214 PartitionDef {
215 columns: vec![partitions.column_name.clone()],
216 exprs: partitions.generate().unwrap(),
217 }
218}
219
220#[derive(Builder)]
222#[builder(pattern = "owned")]
223pub struct CreatePhysicalTableExprGenerator<R: Rng + 'static> {
224 #[builder(default = "Box::new(WordGenerator)")]
225 name_generator: Box<dyn Random<Ident, R>>,
226 #[builder(default = "false")]
227 if_not_exists: bool,
228 #[builder(default = "0")]
229 partition: usize,
230 #[builder(default = "false")]
231 partition_column: bool,
232 #[builder(default, setter(into))]
233 with_clause: HashMap<String, String>,
234}
235
236impl<R: Rng + 'static> Generator<CreateTableExpr, R> for CreatePhysicalTableExprGenerator<R> {
237 type Error = Error;
238
239 fn generate(&self, rng: &mut R) -> Result<CreateTableExpr> {
240 let mut options = HashMap::with_capacity(self.with_clause.len() + 1);
241 options.insert("physical_metric_table".to_string(), Value::from(""));
242 for (key, value) in &self.with_clause {
243 options.insert(key.clone(), Value::from(value.clone()));
244 }
245
246 let mut columns = vec![
247 Column {
248 name: Ident::new("ts"),
249 column_type: ConcreteDataType::timestamp_millisecond_datatype(),
250 options: vec![ColumnOption::TimeIndex],
251 },
252 Column {
253 name: Ident::new("val"),
254 column_type: ConcreteDataType::float64_datatype(),
255 options: vec![],
256 },
257 ];
258
259 let mut partition = None;
260 let mut primary_keys = vec![];
261 if self.partition > 1 || self.partition_column {
262 columns.push(metric_partition_column());
263 primary_keys.push(columns.len() - 1);
264 }
265 if self.partition > 1 {
266 partition = Some(generate_metric_partition_def(self.partition));
267 }
268
269 Ok(CreateTableExpr {
270 table_name: self.name_generator.generate(rng),
271 columns,
272 if_not_exists: self.if_not_exists,
273 partition,
274 engine: "metric".to_string(),
275 options,
276 primary_keys,
277 })
278 }
279}
280
281#[derive(Builder)]
283#[builder(pattern = "owned")]
284pub struct CreateLogicalTableExprGenerator<R: Rng + 'static> {
285 physical_table_ctx: TableContextRef,
286 labels: usize,
287 if_not_exists: bool,
288 #[builder(default = "true")]
289 include_partition_column: bool,
290 #[builder(default = "Box::new(WordGenerator)")]
291 name_generator: Box<dyn Random<Ident, R>>,
292}
293
294impl<R: Rng + 'static> Generator<CreateTableExpr, R> for CreateLogicalTableExprGenerator<R> {
295 type Error = Error;
296
297 fn generate(&self, rng: &mut R) -> Result<CreateTableExpr> {
298 ensure!(
300 self.physical_table_ctx.columns.len() >= 2,
301 error::UnexpectedSnafu {
302 violated: "The physical table must have at least two columns"
303 }
304 );
305
306 let logical_table_name = self
308 .physical_table_ctx
309 .generate_unique_table_name(rng, self.name_generator.as_ref());
310 let mut physical_columns = self.physical_table_ctx.columns.clone();
311 if !self.include_partition_column
312 && let Some(partition_def) = &self.physical_table_ctx.partition
313 {
314 physical_columns.retain(|column| !partition_def.columns.contains(&column.name));
315 }
316
317 let mut logical_table = CreateTableExpr {
318 table_name: logical_table_name,
319 columns: physical_columns,
320 if_not_exists: self.if_not_exists,
321 partition: None,
322 engine: "metric".to_string(),
323 options: [(
324 "on_physical_table".to_string(),
325 self.physical_table_ctx.name.value.clone().into(),
326 )]
327 .into(),
328 primary_keys: vec![],
329 };
330
331 let column_names = self.name_generator.choose(rng, self.labels);
332 logical_table.columns.extend(generate_columns(
333 rng,
334 column_names,
335 &StringColumnTypeGenerator,
336 Box::new(primary_key_options_generator),
337 ));
338
339 let mut primary_keys = vec![];
342 for (idx, column) in logical_table.columns.iter().enumerate() {
343 if column.is_primary_key() {
344 primary_keys.push(idx);
345 }
346 }
347 primary_keys.shuffle(rng);
348 logical_table.primary_keys = primary_keys;
349
350 Ok(logical_table)
351 }
352}
353
354#[derive(Builder)]
355#[builder(default, pattern = "owned")]
356pub struct CreateDatabaseExprGenerator<R: Rng + 'static> {
357 #[builder(setter(into))]
358 database_name: String,
359 name_generator: Box<dyn Random<Ident, R>>,
360 if_not_exists: bool,
361}
362
363impl<R: Rng + 'static> Default for CreateDatabaseExprGenerator<R> {
364 fn default() -> Self {
365 Self {
366 database_name: String::new(),
367 name_generator: Box::new(MappedGenerator::new(WordGenerator, random_capitalize_map)),
368 if_not_exists: false,
369 }
370 }
371}
372
373impl<R: Rng + 'static> Generator<CreateDatabaseExpr, R> for CreateDatabaseExprGenerator<R> {
374 type Error = Error;
375
376 fn generate(&self, rng: &mut R) -> Result<CreateDatabaseExpr> {
377 let mut builder = CreateDatabaseExprBuilder::default();
378 builder.if_not_exists(self.if_not_exists);
379 if self.database_name.is_empty() {
380 builder.database_name(self.name_generator.generate(rng));
381 } else {
382 builder.database_name(self.database_name.clone());
383 }
384 builder.build().context(error::BuildCreateDatabaseExprSnafu)
385 }
386}
387
388#[cfg(test)]
389mod tests {
390 use std::sync::Arc;
391
392 use datatypes::data_type::ConcreteDataType;
393 use datatypes::value::Value;
394 use rand::SeedableRng;
395
396 use super::*;
397 use crate::context::TableContext;
398 use crate::ir::PARTIBLE_DATA_TYPES;
399
400 #[test]
401 fn test_float64() {
402 let value = Value::from(0.047318541668048164);
403 assert_eq!("0.047318541668048164", value.to_string());
404 let value: f64 = "0.047318541668048164".parse().unwrap();
405 assert_eq!("0.047318541668048164", value.to_string());
406 }
407
408 #[test]
409 fn test_create_table_expr_generator() {
410 let mut rng = rand::rng();
411
412 let expr = CreateTableExprGeneratorBuilder::default()
413 .columns(10)
414 .partition(3)
415 .if_not_exists(true)
416 .engine("mito2")
417 .build()
418 .unwrap()
419 .generate(&mut rng)
420 .unwrap();
421 assert_eq!(expr.engine, "mito2");
422 assert!(expr.if_not_exists);
423 assert_eq!(expr.columns.len(), 10);
424 assert_eq!(expr.partition.unwrap().exprs.len(), 3);
425
426 let expr = CreateTableExprGeneratorBuilder::default()
427 .columns(10)
428 .partition(1)
429 .build()
430 .unwrap()
431 .generate(&mut rng)
432 .unwrap();
433 assert_eq!(expr.columns.len(), 10);
434 assert!(expr.partition.is_none());
435
436 let expr = CreateTableExprGeneratorBuilder::default()
437 .columns(10)
438 .partition(1)
439 .partition_column(true)
440 .build()
441 .unwrap()
442 .generate(&mut rng)
443 .unwrap();
444 assert_eq!(expr.columns.len(), 10);
445 assert!(expr.partition.is_none());
446 assert!(PARTIBLE_DATA_TYPES.contains(&expr.columns[0].column_type));
447 }
448
449 #[test]
450 fn test_create_table_expr_generator_deterministic() {
451 let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(0);
452 let expr = CreateTableExprGeneratorBuilder::default()
453 .columns(10)
454 .partition(3)
455 .if_not_exists(true)
456 .engine("mito2")
457 .build()
458 .unwrap()
459 .generate(&mut rng)
460 .unwrap();
461
462 let serialized = serde_json::to_string(&expr).unwrap();
463 let expected = r#"{"table_name":{"value":"quasi","quote_style":null},"columns":[{"name":{"value":"mOLEsTIAs","quote_style":null},"column_type":{"Float64":{}},"options":["PrimaryKey","Null"]},{"name":{"value":"CUMQUe","quote_style":null},"column_type":{"Timestamp":{"Second":null}},"options":["TimeIndex"]},{"name":{"value":"NaTus","quote_style":null},"column_type":{"Int64":{}},"options":[]},{"name":{"value":"EXPeDITA","quote_style":null},"column_type":{"Float64":{}},"options":[]},{"name":{"value":"ImPEDiT","quote_style":null},"column_type":{"Float32":{}},"options":[{"DefaultValue":{"Float32":0.56425774}}]},{"name":{"value":"ADIpisci","quote_style":null},"column_type":{"Float32":{}},"options":["PrimaryKey"]},{"name":{"value":"deBITIs","quote_style":null},"column_type":{"Float32":{}},"options":[{"DefaultValue":{"Float32":0.31315368}}]},{"name":{"value":"toTaM","quote_style":null},"column_type":{"Int32":{}},"options":["NotNull"]},{"name":{"value":"QuI","quote_style":null},"column_type":{"Float32":{}},"options":[{"DefaultValue":{"Float32":0.39941502}}]},{"name":{"value":"INVeNtOre","quote_style":null},"column_type":{"Boolean":null},"options":["PrimaryKey"]}],"if_not_exists":true,"partition":{"columns":[{"value":"mOLEsTIAs","quote_style":null}],"exprs":[{"lhs":{"Column":"mOLEsTIAs"},"op":"Lt","rhs":{"Value":{"Float64":5.992310449541053e+307}}},{"lhs":{"Expr":{"lhs":{"Column":"mOLEsTIAs"},"op":"GtEq","rhs":{"Value":{"Float64":5.992310449541053e+307}}}},"op":"And","rhs":{"Expr":{"lhs":{"Column":"mOLEsTIAs"},"op":"Lt","rhs":{"Value":{"Float64":1.1984620899082105e+308}}}}},{"lhs":{"Column":"mOLEsTIAs"},"op":"GtEq","rhs":{"Value":{"Float64":1.1984620899082105e+308}}}]},"engine":"mito2","options":{},"primary_keys":[0,5,9]}"#;
464 assert_eq!(expected, serialized);
465 }
466
467 #[test]
468 fn test_create_logical_table_expr_generator() {
469 let mut rng = rand::rng();
470
471 let physical_table_expr = CreatePhysicalTableExprGeneratorBuilder::default()
472 .if_not_exists(false)
473 .build()
474 .unwrap()
475 .generate(&mut rng)
476 .unwrap();
477 assert_eq!(physical_table_expr.engine, "metric");
478 assert_eq!(physical_table_expr.columns.len(), 2);
479
480 let physical_ts = physical_table_expr.columns.iter().position(|column| {
481 column
482 .options
483 .iter()
484 .any(|option| option == &ColumnOption::TimeIndex)
485 });
486 let physical_ts_name = physical_table_expr.columns[physical_ts.unwrap()]
487 .name
488 .value
489 .clone();
490
491 let physical_table_ctx = Arc::new(TableContext::from(&physical_table_expr));
492
493 let logical_table_expr = CreateLogicalTableExprGeneratorBuilder::default()
494 .physical_table_ctx(physical_table_ctx)
495 .labels(5)
496 .if_not_exists(false)
497 .build()
498 .unwrap()
499 .generate(&mut rng)
500 .unwrap();
501 let logical_ts = logical_table_expr.columns.iter().position(|column| {
502 column
503 .options
504 .iter()
505 .any(|option| option == &ColumnOption::TimeIndex)
506 });
507 let logical_ts_name = logical_table_expr.columns[logical_ts.unwrap()]
508 .name
509 .value
510 .clone();
511
512 assert_eq!(logical_table_expr.engine, "metric");
513 assert_eq!(logical_table_expr.columns.len(), 7);
514 assert_eq!(logical_ts_name, physical_ts_name);
515 assert!(logical_table_expr.columns.iter().all(|column| {
516 column.column_type != ConcreteDataType::string_datatype()
517 || column
518 .options
519 .iter()
520 .any(|option| option == &ColumnOption::PrimaryKey)
521 }));
522 }
523
524 #[test]
525 fn test_create_physical_table_expr_generator_with_partition() {
526 let mut rng = rand::rng();
527 let physical_table_expr = CreatePhysicalTableExprGeneratorBuilder::default()
528 .partition(3)
529 .if_not_exists(false)
530 .build()
531 .unwrap()
532 .generate(&mut rng)
533 .unwrap();
534
535 assert_eq!(physical_table_expr.engine, "metric");
536 assert!(physical_table_expr.partition.is_some());
537 assert_eq!(physical_table_expr.partition.unwrap().exprs.len(), 3);
538 }
539
540 #[test]
541 fn test_create_physical_table_expr_generator_with_partition_column() {
542 let mut rng = rand::rng();
543 let physical_table_expr = CreatePhysicalTableExprGeneratorBuilder::default()
544 .partition(1)
545 .partition_column(true)
546 .if_not_exists(false)
547 .build()
548 .unwrap()
549 .generate(&mut rng)
550 .unwrap();
551
552 assert_eq!(physical_table_expr.engine, "metric");
553 assert!(physical_table_expr.partition.is_none());
554 assert_eq!(physical_table_expr.columns.len(), 3);
555 assert_eq!(physical_table_expr.columns[2].name, Ident::new("host"));
556 assert_eq!(physical_table_expr.primary_keys, vec![2]);
557 }
558
559 #[test]
560 fn test_create_logical_table_expr_generator_without_partition_column() {
561 let mut rng = rand::rng();
562 let physical_table_expr = CreatePhysicalTableExprGeneratorBuilder::default()
563 .partition(3)
564 .if_not_exists(false)
565 .build()
566 .unwrap()
567 .generate(&mut rng)
568 .unwrap();
569 let partition_columns = physical_table_expr
570 .partition
571 .as_ref()
572 .unwrap()
573 .columns
574 .clone();
575 let physical_table_ctx = Arc::new(TableContext::from(&physical_table_expr));
576
577 let logical_table_expr = CreateLogicalTableExprGeneratorBuilder::default()
578 .physical_table_ctx(physical_table_ctx)
579 .labels(3)
580 .include_partition_column(false)
581 .if_not_exists(false)
582 .build()
583 .unwrap()
584 .generate(&mut rng)
585 .unwrap();
586
587 assert!(
588 logical_table_expr
589 .columns
590 .iter()
591 .all(|column| !partition_columns.contains(&column.name))
592 );
593 }
594
595 #[test]
596 fn test_create_logical_table_expr_generator_deterministic() {
597 let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(0);
598 let physical_table_expr = CreatePhysicalTableExprGeneratorBuilder::default()
599 .if_not_exists(false)
600 .build()
601 .unwrap()
602 .generate(&mut rng)
603 .unwrap();
604 let physical_table_serialized = serde_json::to_string(&physical_table_expr).unwrap();
605 let physical_table_expected = r#"{"table_name":{"value":"expedita","quote_style":null},"columns":[{"name":{"value":"ts","quote_style":null},"column_type":{"Timestamp":{"Millisecond":null}},"options":["TimeIndex"]},{"name":{"value":"val","quote_style":null},"column_type":{"Float64":{}},"options":[]}],"if_not_exists":false,"partition":null,"engine":"metric","options":{"physical_metric_table":{"String":""}},"primary_keys":[]}"#;
606 assert_eq!(physical_table_expected, physical_table_serialized);
607
608 let physical_table_ctx = Arc::new(TableContext::from(&physical_table_expr));
609
610 let logical_table_expr = CreateLogicalTableExprGeneratorBuilder::default()
611 .physical_table_ctx(physical_table_ctx)
612 .labels(5)
613 .if_not_exists(false)
614 .build()
615 .unwrap()
616 .generate(&mut rng)
617 .unwrap();
618
619 let logical_table_serialized = serde_json::to_string(&logical_table_expr).unwrap();
620 let logical_table_expected = r#"{"table_name":{"value":"impedit","quote_style":null},"columns":[{"name":{"value":"ts","quote_style":null},"column_type":{"Timestamp":{"Millisecond":null}},"options":["TimeIndex"]},{"name":{"value":"val","quote_style":null},"column_type":{"Float64":{}},"options":[]},{"name":{"value":"totam","quote_style":null},"column_type":{"String":{"size_type":"Utf8"}},"options":["PrimaryKey"]},{"name":{"value":"cumque","quote_style":null},"column_type":{"String":{"size_type":"Utf8"}},"options":["PrimaryKey"]},{"name":{"value":"natus","quote_style":null},"column_type":{"String":{"size_type":"Utf8"}},"options":["PrimaryKey"]},{"name":{"value":"molestias","quote_style":null},"column_type":{"String":{"size_type":"Utf8"}},"options":["PrimaryKey"]},{"name":{"value":"qui","quote_style":null},"column_type":{"String":{"size_type":"Utf8"}},"options":["PrimaryKey"]}],"if_not_exists":false,"partition":null,"engine":"metric","options":{"on_physical_table":{"String":"expedita"}},"primary_keys":[4,2,3,6,5]}"#;
621 assert_eq!(logical_table_expected, logical_table_serialized);
622 }
623
624 #[test]
625 fn test_create_database_expr_generator() {
626 let mut rng = rand::rng();
627
628 let expr = CreateDatabaseExprGeneratorBuilder::default()
629 .if_not_exists(true)
630 .build()
631 .unwrap()
632 .generate(&mut rng)
633 .unwrap();
634 assert!(expr.if_not_exists);
635 }
636
637 #[test]
638 fn test_create_database_expr_generator_deterministic() {
639 let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(0);
640 let expr = CreateDatabaseExprGeneratorBuilder::default()
641 .if_not_exists(true)
642 .build()
643 .unwrap()
644 .generate(&mut rng)
645 .unwrap();
646
647 let serialized = serde_json::to_string(&expr).unwrap();
648 let expected =
649 r#"{"database_name":{"value":"EXPediTA","quote_style":null},"if_not_exists":true}"#;
650 assert_eq!(expected, serialized);
651 }
652}