diff --git a/tests-fuzz/src/generator.rs b/tests-fuzz/src/generator.rs index f4da5ae2353a..5c3e4a91433b 100644 --- a/tests-fuzz/src/generator.rs +++ b/tests-fuzz/src/generator.rs @@ -20,6 +20,7 @@ pub mod select_expr; use std::fmt; use datatypes::data_type::ConcreteDataType; +use datatypes::types::TimestampType; use datatypes::value::Value; use rand::Rng; @@ -40,6 +41,8 @@ pub type ConcreteDataTypeGenerator = Box>; pub type ValueGenerator = Box>) -> Value>; +pub type TsValueGenerator = Box Value>; + pub trait Generator { type Error: Sync + Send + fmt::Debug; diff --git a/tests-fuzz/src/generator/insert_expr.rs b/tests-fuzz/src/generator/insert_expr.rs index 2549e6bdd01a..286a11d75354 100644 --- a/tests-fuzz/src/generator/insert_expr.rs +++ b/tests-fuzz/src/generator/insert_expr.rs @@ -19,12 +19,13 @@ use derive_builder::Builder; use rand::seq::SliceRandom; use rand::Rng; +use super::TsValueGenerator; use crate::context::TableContextRef; use crate::error::{Error, Result}; use crate::fake::WordGenerator; use crate::generator::{Generator, Random, ValueGenerator}; use crate::ir::insert_expr::{InsertIntoExpr, RowValue}; -use crate::ir::{generate_random_value, Ident}; +use crate::ir::{generate_random_timestamp, generate_random_value, Ident}; /// Generates [InsertIntoExpr]. #[derive(Builder)] @@ -39,6 +40,8 @@ pub struct InsertExprGenerator { word_generator: Box>, #[builder(default = "Box::new(generate_random_value)")] value_generator: ValueGenerator, + #[builder(default = "Box::new(generate_random_timestamp)")] + ts_value_generator: TsValueGenerator, #[builder(default)] _phantom: PhantomData, } @@ -82,12 +85,18 @@ impl Generator for InsertExprGenerator { row.push(RowValue::Default); continue; } - - row.push(RowValue::Value((self.value_generator)( - rng, - &column.column_type, - Some(self.word_generator.as_ref()), - ))); + if column.is_time_index() { + row.push(RowValue::Value((self.ts_value_generator)( + rng, + column.timestamp_type().unwrap(), + ))); + } else { + row.push(RowValue::Value((self.value_generator)( + rng, + &column.column_type, + Some(self.word_generator.as_ref()), + ))); + } } values_list.push(row); diff --git a/tests-fuzz/src/ir.rs b/tests-fuzz/src/ir.rs index 01d2cd430981..e88cfc81df1a 100644 --- a/tests-fuzz/src/ir.rs +++ b/tests-fuzz/src/ir.rs @@ -21,6 +21,8 @@ pub(crate) mod select_expr; use core::fmt; use std::collections::HashMap; +use std::sync::atomic::{AtomicI64, Ordering}; +use std::sync::Arc; pub use alter_expr::AlterTableExpr; use common_time::{Date, DateTime, Timestamp}; @@ -119,38 +121,31 @@ pub fn generate_random_value( }, ConcreteDataType::Date(_) => generate_random_date(rng), ConcreteDataType::DateTime(_) => generate_random_datetime(rng), - &ConcreteDataType::Timestamp(ts_type) => generate_random_timestamp(rng, ts_type), _ => unimplemented!("unsupported type: {datatype}"), } } -/// Generates a random [Value] for MySQL. -pub fn generate_random_value_for_mysql( - rng: &mut R, - datatype: &ConcreteDataType, - random_str: Option<&dyn Random>, -) -> Value { - match datatype { - &ConcreteDataType::Boolean(_) => Value::from(rng.gen::()), - ConcreteDataType::Int16(_) => Value::from(rng.gen::()), - ConcreteDataType::Int32(_) => Value::from(rng.gen::()), - ConcreteDataType::Int64(_) => Value::from(rng.gen::()), - ConcreteDataType::Float32(_) => Value::from(rng.gen::()), - ConcreteDataType::Float64(_) => Value::from(rng.gen::()), - ConcreteDataType::String(_) => match random_str { - Some(random) => Value::from(random.gen(rng).value), - None => Value::from(rng.gen::().to_string()), - }, - ConcreteDataType::Date(_) => generate_random_date(rng), - ConcreteDataType::DateTime(_) => generate_random_datetime(rng), - &ConcreteDataType::Timestamp(ts_type) => generate_random_timestamp_for_mysql(rng, ts_type), - - _ => unimplemented!("unsupported type: {datatype}"), +/// Generate monotonically increasing timestamps for MySQL. +pub fn generate_unique_timestamp_for_mysql( + base: i64, +) -> impl Fn(&mut R, TimestampType) -> Value { + let base = Arc::new(AtomicI64::new(base)); + + move |_rng, ts_type| -> Value { + let value = base.fetch_add(1, Ordering::Relaxed); + let v = match ts_type { + TimestampType::Second(_) => Timestamp::new_second(1 + value), + TimestampType::Millisecond(_) => Timestamp::new_millisecond(1000 + value), + TimestampType::Microsecond(_) => Timestamp::new_microsecond(1_000_000 + value), + TimestampType::Nanosecond(_) => Timestamp::new_nanosecond(1_000_000_000 + value), + }; + Value::from(v) } } -fn generate_random_timestamp(rng: &mut R, ts_type: TimestampType) -> Value { +/// Generate random timestamps. +pub fn generate_random_timestamp(rng: &mut R, ts_type: TimestampType) -> Value { let v = match ts_type { TimestampType::Second(_) => { let min = i64::from(Timestamp::MIN_SECOND); @@ -181,7 +176,7 @@ fn generate_random_timestamp(rng: &mut R, ts_type: TimestampType) -> Val } // MySQL supports timestamp from '1970-01-01 00:00:01.000000' to '2038-01-19 03:14:07.499999' -fn generate_random_timestamp_for_mysql(rng: &mut R, ts_type: TimestampType) -> Value { +pub fn generate_random_timestamp_for_mysql(rng: &mut R, ts_type: TimestampType) -> Value { let v = match ts_type { TimestampType::Second(_) => { let min = 1; @@ -298,6 +293,15 @@ pub struct Column { } impl Column { + /// Returns [TimestampType] if it's [ColumnOption::TimeIndex] [Column]. + pub fn timestamp_type(&self) -> Option { + if let ConcreteDataType::Timestamp(ts_type) = self.column_type { + Some(ts_type) + } else { + None + } + } + /// Returns true if it's [ColumnOption::TimeIndex] [Column]. pub fn is_time_index(&self) -> bool { self.options diff --git a/tests-fuzz/targets/fuzz_insert.rs b/tests-fuzz/targets/fuzz_insert.rs index 73baf5a39377..fdb258775187 100644 --- a/tests-fuzz/targets/fuzz_insert.rs +++ b/tests-fuzz/targets/fuzz_insert.rs @@ -33,8 +33,8 @@ use tests_fuzz::generator::create_expr::CreateTableExprGeneratorBuilder; use tests_fuzz::generator::insert_expr::InsertExprGeneratorBuilder; use tests_fuzz::generator::Generator; use tests_fuzz::ir::{ - generate_random_value_for_mysql, replace_default, CreateTableExpr, InsertIntoExpr, - MySQLTsColumnTypeGenerator, + generate_random_timestamp_for_mysql, generate_random_value, replace_default, CreateTableExpr, + InsertIntoExpr, MySQLTsColumnTypeGenerator, }; use tests_fuzz::translator::mysql::create_expr::CreateTableExprTranslator; use tests_fuzz::translator::mysql::insert_expr::InsertIntoExprTranslator; @@ -101,7 +101,8 @@ fn generate_insert_expr( .table_ctx(table_ctx) .omit_column_list(omit_column_list) .rows(input.rows) - .value_generator(Box::new(generate_random_value_for_mysql)) + .value_generator(Box::new(generate_random_value)) + .ts_value_generator(Box::new(generate_random_timestamp_for_mysql)) .build() .unwrap(); insert_generator.generate(rng) diff --git a/tests-fuzz/targets/fuzz_insert_logical_table.rs b/tests-fuzz/targets/fuzz_insert_logical_table.rs index fe7a25c6761d..de518f284ac0 100644 --- a/tests-fuzz/targets/fuzz_insert_logical_table.rs +++ b/tests-fuzz/targets/fuzz_insert_logical_table.rs @@ -36,7 +36,8 @@ use tests_fuzz::generator::create_expr::{ use tests_fuzz::generator::insert_expr::InsertExprGeneratorBuilder; use tests_fuzz::generator::Generator; use tests_fuzz::ir::{ - generate_random_value_for_mysql, replace_default, CreateTableExpr, InsertIntoExpr, + generate_random_timestamp_for_mysql, generate_random_value, replace_default, CreateTableExpr, + InsertIntoExpr, }; use tests_fuzz::translator::mysql::create_expr::CreateTableExprTranslator; use tests_fuzz::translator::mysql::insert_expr::InsertIntoExprTranslator; @@ -112,7 +113,8 @@ fn generate_insert_expr( .omit_column_list(false) .table_ctx(table_ctx) .rows(rows) - .value_generator(Box::new(generate_random_value_for_mysql)) + .value_generator(Box::new(generate_random_value)) + .ts_value_generator(Box::new(generate_random_timestamp_for_mysql)) .build() .unwrap(); insert_generator.generate(rng)