From e81786aea0764ce5bab61566713fc8647186f6bc Mon Sep 17 00:00:00 2001 From: Kumar Ujjawal Date: Sat, 18 Apr 2026 17:22:11 +0530 Subject: [PATCH] fix(tlp): avoid false positives in TLP --- src/oracle/oracle_impl_tlp_having.rs | 3 +- src/oracle/oracle_impl_tlp_where.rs | 3 +- src/query_generator/expr_gen.rs | 204 +++++++++++++++++++++++- src/query_generator/stmt_select_def.rs | 22 ++- tests/integration_test.rs | 212 ++++++++++++++++--------- 5 files changed, 366 insertions(+), 78 deletions(-) diff --git a/src/oracle/oracle_impl_tlp_having.rs b/src/oracle/oracle_impl_tlp_having.rs index 5735742..1beeba6 100644 --- a/src/oracle/oracle_impl_tlp_having.rs +++ b/src/oracle/oracle_impl_tlp_having.rs @@ -58,7 +58,8 @@ impl Oracle for TlpHavingOracle { .with_allow_derived_tables(false) .with_max_table_count(1) .with_enable_group_by_clause(InclusionConfig::Always(true)) - .with_enable_having_clause(InclusionConfig::Always(true)); + .with_enable_having_clause(InclusionConfig::Always(true)) + .with_valid_boolean_exprs_only(true); let stmt = stmt_builder.generate_stmt()?; let source_sql = stmt.to_from_join_sql()?; diff --git a/src/oracle/oracle_impl_tlp_where.rs b/src/oracle/oracle_impl_tlp_where.rs index d450c5c..625d92a 100644 --- a/src/oracle/oracle_impl_tlp_where.rs +++ b/src/oracle/oracle_impl_tlp_where.rs @@ -49,7 +49,8 @@ impl Oracle for TlpWhereOracle { InclusionConfig::Always(false), ) .with_allow_derived_tables(false) - .with_max_table_count(1); + .with_max_table_count(1) + .with_valid_boolean_exprs_only(true); let stmt = stmt_builder.generate_stmt()?; let source_sql = stmt.to_from_join_sql()?; diff --git a/src/query_generator/expr_gen.rs b/src/query_generator/expr_gen.rs index f632ccf..65da6c4 100644 --- a/src/query_generator/expr_gen.rs +++ b/src/query_generator/expr_gen.rs @@ -1,10 +1,16 @@ use std::sync::Arc; -use datafusion::{arrow::datatypes::DataType, common::Column, prelude::Expr, sql::TableReference}; +use datafusion::{ + arrow::datatypes::DataType, + common::Column, + logical_expr::{BinaryExpr, Operator}, + prelude::Expr, + sql::TableReference, +}; use rand::{Rng, rngs::StdRng}; use crate::{ - common::{FuzzerDataType, LogicalTable, rng::rng_from_seed}, + common::{FuzzerDataType, LogicalTable, get_available_data_types, rng::rng_from_seed}, fuzz_context::GlobalContext, }; @@ -105,6 +111,28 @@ impl ExprGenerator { } } + /// Generate a boolean expression that stays within simple, well-typed patterns. + /// This is used by TLP oracles to avoid false positives from intentionally invalid expressions. + pub fn generate_valid_boolean_expr(&mut self, cur_level: u32) -> Expr { + if cur_level >= self.max_level || self.rng.random_bool(0.5) { + return self.generate_valid_boolean_leaf(); + } + + match self.rng.random_range(0..3) { + 0 => Expr::BinaryExpr(BinaryExpr::new( + Box::new(self.generate_valid_boolean_expr(cur_level + 1)), + Operator::And, + Box::new(self.generate_valid_boolean_expr(cur_level + 1)), + )), + 1 => Expr::BinaryExpr(BinaryExpr::new( + Box::new(self.generate_valid_boolean_expr(cur_level + 1)), + Operator::Or, + Box::new(self.generate_valid_boolean_expr(cur_level + 1)), + )), + _ => self.generate_valid_boolean_leaf(), + } + } + // Generate either a constant value or a column reference fn generate_leaf_expr(&mut self, target_type: DataType) -> Expr { // For certain chance: try to generate a column reference if available @@ -126,6 +154,72 @@ impl ExprGenerator { } } + fn generate_valid_boolean_leaf(&mut self) -> Expr { + match self.rng.random_range(0..4) { + 0 => self.generate_leaf_expr(DataType::Boolean), + 1 => self.generate_valid_equality_expr(), + 2 => self.generate_valid_ordering_expr(), + _ => self.generate_valid_like_expr(), + } + } + + fn generate_valid_equality_expr(&mut self) -> Expr { + let comparable_types: Vec = get_available_data_types() + .iter() + .filter(|ty| !matches!(ty, FuzzerDataType::IntervalMonthDayNano)) + .map(|ty| ty.to_datafusion_type()) + .collect(); + let data_type = comparable_types[self.rng.random_range(0..comparable_types.len())].clone(); + let left = self.generate_leaf_expr(data_type.clone()); + let right = self.generate_leaf_expr(data_type); + let operator = match self.rng.random_range(0..2) { + 0 => Operator::Eq, + _ => Operator::NotEq, + }; + + Expr::BinaryExpr(BinaryExpr::new(Box::new(left), operator, Box::new(right))) + } + + fn generate_valid_ordering_expr(&mut self) -> Expr { + let orderable_types: Vec = get_available_data_types() + .iter() + .filter(|ty| { + ty.is_numeric() + || matches!( + ty, + FuzzerDataType::Date32 + | FuzzerDataType::Time64Nanosecond + | FuzzerDataType::Timestamp + ) + }) + .map(|ty| ty.to_datafusion_type()) + .collect(); + let data_type = orderable_types[self.rng.random_range(0..orderable_types.len())].clone(); + let left = self.generate_leaf_expr(data_type.clone()); + let right = self.generate_leaf_expr(data_type); + let operator = match self.rng.random_range(0..4) { + 0 => Operator::Lt, + 1 => Operator::LtEq, + 2 => Operator::Gt, + _ => Operator::GtEq, + }; + + Expr::BinaryExpr(BinaryExpr::new(Box::new(left), operator, Box::new(right))) + } + + fn generate_valid_like_expr(&mut self) -> Expr { + let left = self.generate_leaf_expr(DataType::Utf8); + let right = self.generate_leaf_expr(DataType::Utf8); + let operator = match self.rng.random_range(0..4) { + 0 => Operator::LikeMatch, + 1 => Operator::ILikeMatch, + 2 => Operator::NotLikeMatch, + _ => Operator::NotILikeMatch, + }; + + Expr::BinaryExpr(BinaryExpr::new(Box::new(left), operator, Box::new(right))) + } + fn get_all_columns_of_type(&self, target_type: DataType) -> Vec { // Use the actual column information from the tables let mut matching_columns = Vec::new(); @@ -165,3 +259,109 @@ impl ExprGenerator { expr_impl.build_expr(child_exprs) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::common::{LogicalColumn, init_available_data_types}; + + #[tokio::test] + async fn valid_boolean_exprs_execute_successfully() { + init_available_data_types(); + let ctx = Arc::new(crate::fuzz_context::GlobalContext::default()); + let session_ctx = ctx.runtime_context.get_session_context(); + + session_ctx + .sql( + "CREATE TABLE t0 ( + b BOOLEAN, + i BIGINT, + f DOUBLE, + d DATE, + tm TIME, + ts TIMESTAMP, + s VARCHAR + )", + ) + .await + .unwrap() + .collect() + .await + .unwrap(); + + session_ctx + .sql( + "INSERT INTO t0 VALUES ( + true, + 1, + 1.5, + CAST('2024-01-01' AS DATE), + CAST('12:00:00' AS TIME), + CAST('2024-01-01T12:00:00' AS TIMESTAMP), + 'abc' + )", + ) + .await + .unwrap() + .collect() + .await + .unwrap(); + + let logical_table = Arc::new(LogicalTable::with_columns( + "t0".to_string(), + vec![ + LogicalColumn { + name: "b".to_string(), + data_type: FuzzerDataType::Boolean, + }, + LogicalColumn { + name: "i".to_string(), + data_type: FuzzerDataType::Int64, + }, + LogicalColumn { + name: "f".to_string(), + data_type: FuzzerDataType::Float64, + }, + LogicalColumn { + name: "d".to_string(), + data_type: FuzzerDataType::Date32, + }, + LogicalColumn { + name: "tm".to_string(), + data_type: FuzzerDataType::Time64Nanosecond, + }, + LogicalColumn { + name: "ts".to_string(), + data_type: FuzzerDataType::Timestamp, + }, + LogicalColumn { + name: "s".to_string(), + data_type: FuzzerDataType::String, + }, + ], + )); + + ctx.runtime_context + .registered_tables + .write() + .unwrap() + .insert("t0".to_string(), Arc::clone(&logical_table)); + + let src_columns = Arc::new(ExprGenerator::tables_to_columns(&[logical_table], &ctx)); + + for seed in 0..32 { + let mut expr_gen = ExprGenerator::new(seed, Arc::clone(&ctx)) + .with_src_columns(Arc::clone(&src_columns)); + let expr = expr_gen.generate_valid_boolean_expr(0); + let expr_sql = crate::common::util::to_sql_string(&expr).unwrap(); + let query = format!("SELECT * FROM t0 WHERE {}", expr_sql); + session_ctx + .sql(&query) + .await + .unwrap() + .collect() + .await + .unwrap(); + } + } +} diff --git a/src/query_generator/stmt_select_def.rs b/src/query_generator/stmt_select_def.rs index 4104ad4..9d60119 100644 --- a/src/query_generator/stmt_select_def.rs +++ b/src/query_generator/stmt_select_def.rs @@ -175,6 +175,8 @@ pub struct SelectStatementBuilder { enable_group_by_clause: InclusionConfig, /// Control whether HAVING clause is generated (requires GROUP BY) enable_having_clause: InclusionConfig, + /// Restrict WHERE/HAVING predicates to well-typed expressions. + valid_boolean_exprs_only: bool, // ==== Intermediate states to build the final select stmt ==== /// Tables in the FROM clause @@ -203,6 +205,7 @@ impl SelectStatementBuilder { enable_join_clause, enable_group_by_clause: InclusionConfig::Always(false), enable_having_clause: InclusionConfig::Always(false), + valid_boolean_exprs_only: false, } } @@ -232,6 +235,12 @@ impl SelectStatementBuilder { self } + /// Restrict WHERE/HAVING predicates to well-typed expressions. + pub fn with_valid_boolean_exprs_only(mut self, valid_boolean_exprs_only: bool) -> Self { + self.valid_boolean_exprs_only = valid_boolean_exprs_only; + self + } + pub fn generate_stmt(&mut self) -> Result { // ==== Pick src tables ==== let src_tables = self.pick_src_tables()?; @@ -414,8 +423,11 @@ impl SelectStatementBuilder { // Decide if the WHERE clause should be generated if self.enable_where_clause.should_enable(Some(&mut self.rng)) { // Generate a boolean expression for the WHERE clause - let where_expr = - expr_gen.generate_random_expr(datafusion::arrow::datatypes::DataType::Boolean, 0); + let where_expr = if self.valid_boolean_exprs_only { + expr_gen.generate_valid_boolean_expr(0) + } else { + expr_gen.generate_random_expr(datafusion::arrow::datatypes::DataType::Boolean, 0) + }; Ok(Some(where_expr)) } else { Ok(None) @@ -473,7 +485,11 @@ impl SelectStatementBuilder { let mut having_expr_gen = ExprGenerator::new(self.rng.next_u64(), self.ctx.clone()) .with_src_columns(Arc::new(group_by_columns)); - let having_expr = having_expr_gen.generate_random_expr(DataType::Boolean, 0); + let having_expr = if self.valid_boolean_exprs_only { + having_expr_gen.generate_valid_boolean_expr(0) + } else { + having_expr_gen.generate_random_expr(DataType::Boolean, 0) + }; Ok(Some(having_expr)) } diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 55a87d9..52251f4 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -114,15 +114,15 @@ fn full_run_logs_expected_queries_for_tlp_where_oracle() -> Result<(), Box NULL) OR (CAST('13:24:10.016648859' AS TIME) > (-44 + -91)))) + WHERE (((':DG&":>T!}[A+i"|/:g8T8x l/uke;?H[1$M2`d' !~~* 'qd~XT}{E1;hPq<&+WFF)F~83HMLB%66-2)*("W`%-`oJ') AND (((53.6511400772695 <> 67.77106443187964) AND ('bM4*grp' ~~ 'lWD|O.[z?XPYv[x"%;uctuB4D4J40CFkdDVIwxY|Jncc@it')) AND true))) UNION ALL SELECT * FROM t0 - WHERE NOT (((NULL > NULL) OR (CAST('13:24:10.016648859' AS TIME) > (-44 + -91)))) + WHERE NOT (((':DG&":>T!}[A+i"|/:g8T8x l/uke;?H[1$M2`d' !~~* 'qd~XT}{E1;hPq<&+WFF)F~83HMLB%66-2)*("W`%-`oJ') AND (((53.6511400772695 <> 67.77106443187964) AND ('bM4*grp' ~~ 'lWD|O.[z?XPYv[x"%;uctuB4D4J40CFkdDVIwxY|Jncc@it')) AND true))) UNION ALL SELECT * FROM t0 - WHERE (((NULL > NULL) OR (CAST('13:24:10.016648859' AS TIME) > (-44 + -91)))) IS NULL + WHERE (((':DG&":>T!}[A+i"|/:g8T8x l/uke;?H[1$M2`d' !~~* 'qd~XT}{E1;hPq<&+WFF)F~83HMLB%66-2)*("W`%-`oJ') AND (((53.6511400772695 <> 67.77106443187964) AND ('bM4*grp' ~~ 'lWD|O.[z?XPYv[x"%;uctuB4D4J40CFkdDVIwxY|Jncc@it')) AND true))) IS NULL === round=1 query=2 oracle=TlpWhereOracle query_seed=310305 === --- statement=1 context=TLP-WHERE all --- @@ -132,15 +132,15 @@ fn full_run_logs_expected_queries_for_tlp_where_oracle() -> Result<(), Box Result<(), Box= NULL))) + UNION ALL + SELECT * + FROM t0 + WHERE NOT ((true OR (-87.08115412367125 >= NULL))) + UNION ALL + SELECT * + FROM t0 + WHERE ((true OR (-87.08115412367125 >= NULL))) IS NULL === round=1 query=5 oracle=TlpWhereOracle query_seed=310308 === --- statement=1 context=TLP-WHERE all --- @@ -168,15 +186,15 @@ fn full_run_logs_expected_queries_for_tlp_where_oracle() -> Result<(), Box0{mqUZdjZE@' ~~* '7;=k9L4l6.-bG6dPLWk-7 ~9azH0^V;7q0S#|%@')) UNION ALL SELECT * FROM t0 - WHERE NOT ((to_char(CAST('2052-04-28' AS DATE), '=B 2v') !~* to_char(INTERVAL '1 MONS -11 DAYS -0.658344865 SECS', to_char(CAST('2056-06-17 08:39:22.305135405 -09:00' AS TIMESTAMP), '9L4l6.-bG6dPLWk-7 ~9azH0^V;7q0S#|%@?MyX"')))) + WHERE NOT (('c%5=YV?=n6")1*=B 2vXZVzj_NYsR>0{mqUZdjZE@' ~~* '7;=k9L4l6.-bG6dPLWk-7 ~9azH0^V;7q0S#|%@')) UNION ALL SELECT * FROM t0 - WHERE ((to_char(CAST('2052-04-28' AS DATE), '=B 2v') !~* to_char(INTERVAL '1 MONS -11 DAYS -0.658344865 SECS', to_char(CAST('2056-06-17 08:39:22.305135405 -09:00' AS TIMESTAMP), '9L4l6.-bG6dPLWk-7 ~9azH0^V;7q0S#|%@?MyX"')))) IS NULL + WHERE (('c%5=YV?=n6")1*=B 2vXZVzj_NYsR>0{mqUZdjZE@' ~~* '7;=k9L4l6.-bG6dPLWk-7 ~9azH0^V;7q0S#|%@')) IS NULL === round=2 query=1 oracle=TlpWhereOracle query_seed=311304 === --- statement=1 context=TLP-WHERE all --- @@ -186,15 +204,15 @@ fn full_run_logs_expected_queries_for_tlp_where_oracle() -> Result<(), Box Result<(), Box Result<(), Box Result<(), Box= ((-7.129738 - 23.446228) % (47.11673 / 88.10098)))) + WHERE (((CAST('2018-03-04 04:07:36.304896253 +09:00' AS TIMESTAMP) = CAST('1983-06-29 22:43:16.586720830 +09:30' AS TIMESTAMP)) OR (130 <> 133))) UNION ALL SELECT * FROM t1 - WHERE NOT ((96 >= ((-7.129738 - 23.446228) % (47.11673 / 88.10098)))) + WHERE NOT (((CAST('2018-03-04 04:07:36.304896253 +09:00' AS TIMESTAMP) = CAST('1983-06-29 22:43:16.586720830 +09:30' AS TIMESTAMP)) OR (130 <> 133))) UNION ALL SELECT * FROM t1 - WHERE ((96 >= ((-7.129738 - 23.446228) % (47.11673 / 88.10098)))) IS NULL + WHERE (((CAST('2018-03-04 04:07:36.304896253 +09:00' AS TIMESTAMP) = CAST('1983-06-29 22:43:16.586720830 +09:30' AS TIMESTAMP)) OR (130 <> 133))) IS NULL === round=2 query=5 oracle=TlpWhereOracle query_seed=311308 === --- statement=1 context=TLP-WHERE all --- @@ -258,25 +276,25 @@ fn full_run_logs_expected_queries_for_tlp_where_oracle() -> Result<(), Box CAST('15:53:52.409553197' AS TIME))) UNION ALL SELECT * FROM t2 - WHERE NOT (true) + WHERE NOT ((CAST('17:12:17.726406411' AS TIME) > CAST('15:53:52.409553197' AS TIME))) UNION ALL SELECT * FROM t2 - WHERE (true) IS NULL + WHERE ((CAST('17:12:17.726406411' AS TIME) > CAST('15:53:52.409553197' AS TIME))) IS NULL "#); - insta::assert_snapshot!(run_output.stats_summary, @r#" + insta::assert_snapshot!(run_output.stats_summary, @r" ============================================================ 🎯 DataFusion Fuzzer - Final Statistics ============================================================ 📊 Execution Summary: • Rounds Completed: 2 - • Queries Executed: 18 - • Query Success Rate: 88.89% - "#); + • Queries Executed: 20 + • Query Success Rate: 100.00% + "); fs::remove_dir_all(&log_dir)?; @@ -323,121 +341,173 @@ fn full_run_logs_expected_queries_for_tlp_having_oracle() -> Result<(), Box= NULL)) + GROUP BY t0.col_t0_3_date32 + + --- statement=2 context=TLP-HAVING p UNION ALL NOT p UNION ALL p IS NULL --- + SELECT t0.col_t0_3_date32 + FROM t0 + WHERE (true OR (-87.08115412367125 >= NULL)) + GROUP BY t0.col_t0_3_date32 + HAVING ((NULL ~~* 'C#u}>F.C')) + UNION ALL + SELECT t0.col_t0_3_date32 + FROM t0 + WHERE (true OR (-87.08115412367125 >= NULL)) + GROUP BY t0.col_t0_3_date32 + HAVING NOT ((NULL ~~* 'C#u}>F.C')) + UNION ALL + SELECT t0.col_t0_3_date32 + FROM t0 + WHERE (true OR (-87.08115412367125 >= NULL)) + GROUP BY t0.col_t0_3_date32 + HAVING ((NULL ~~* 'C#u}>F.C')) IS NULL === round=1 query=5 oracle=TlpHavingOracle query_seed=310308 === --- statement=1 context=TLP-HAVING all groups --- SELECT t0.col_t0_2_float32, t0.col_t0_3_date32, t0.col_t0_1_decimal128 FROM t0 - WHERE (to_char(CAST('2052-04-28' AS DATE), '=B 2v') !~* to_char(INTERVAL '1 MONS -11 DAYS -0.658344865 SECS', to_char(CAST('2056-06-17 08:39:22.305135405 -09:00' AS TIMESTAMP), '9L4l6.-bG6dPLWk-7 ~9azH0^V;7q0S#|%@?MyX"'))) + WHERE ('c%5=YV?=n6")1*=B 2vXZVzj_NYsR>0{mqUZdjZE@' ~~* '7;=k9L4l6.-bG6dPLWk-7 ~9azH0^V;7q0S#|%@') GROUP BY t0.col_t0_2_float32, t0.col_t0_3_date32, t0.col_t0_1_decimal128 --- statement=2 context=TLP-HAVING p UNION ALL NOT p UNION ALL p IS NULL --- SELECT t0.col_t0_2_float32, t0.col_t0_3_date32, t0.col_t0_1_decimal128 FROM t0 - WHERE (to_char(CAST('2052-04-28' AS DATE), '=B 2v') !~* to_char(INTERVAL '1 MONS -11 DAYS -0.658344865 SECS', to_char(CAST('2056-06-17 08:39:22.305135405 -09:00' AS TIMESTAMP), '9L4l6.-bG6dPLWk-7 ~9azH0^V;7q0S#|%@?MyX"'))) + WHERE ('c%5=YV?=n6")1*=B 2vXZVzj_NYsR>0{mqUZdjZE@' ~~* '7;=k9L4l6.-bG6dPLWk-7 ~9azH0^V;7q0S#|%@') GROUP BY t0.col_t0_2_float32, t0.col_t0_3_date32, t0.col_t0_1_decimal128 - HAVING ((to_char(INTERVAL '-7 MONS 29 DAYS -0.000000001 SECS', '%X `B') !~* '0SsYa@-p]yc`qTL8PvF #c;Tei9))DXs:^wgv[')) + HAVING (false) UNION ALL SELECT t0.col_t0_2_float32, t0.col_t0_3_date32, t0.col_t0_1_decimal128 FROM t0 - WHERE (to_char(CAST('2052-04-28' AS DATE), '=B 2v') !~* to_char(INTERVAL '1 MONS -11 DAYS -0.658344865 SECS', to_char(CAST('2056-06-17 08:39:22.305135405 -09:00' AS TIMESTAMP), '9L4l6.-bG6dPLWk-7 ~9azH0^V;7q0S#|%@?MyX"'))) + WHERE ('c%5=YV?=n6")1*=B 2vXZVzj_NYsR>0{mqUZdjZE@' ~~* '7;=k9L4l6.-bG6dPLWk-7 ~9azH0^V;7q0S#|%@') GROUP BY t0.col_t0_2_float32, t0.col_t0_3_date32, t0.col_t0_1_decimal128 - HAVING NOT ((to_char(INTERVAL '-7 MONS 29 DAYS -0.000000001 SECS', '%X `B') !~* '0SsYa@-p]yc`qTL8PvF #c;Tei9))DXs:^wgv[')) + HAVING NOT (false) UNION ALL SELECT t0.col_t0_2_float32, t0.col_t0_3_date32, t0.col_t0_1_decimal128 FROM t0 - WHERE (to_char(CAST('2052-04-28' AS DATE), '=B 2v') !~* to_char(INTERVAL '1 MONS -11 DAYS -0.658344865 SECS', to_char(CAST('2056-06-17 08:39:22.305135405 -09:00' AS TIMESTAMP), '9L4l6.-bG6dPLWk-7 ~9azH0^V;7q0S#|%@?MyX"'))) + WHERE ('c%5=YV?=n6")1*=B 2vXZVzj_NYsR>0{mqUZdjZE@' ~~* '7;=k9L4l6.-bG6dPLWk-7 ~9azH0^V;7q0S#|%@') GROUP BY t0.col_t0_2_float32, t0.col_t0_3_date32, t0.col_t0_1_decimal128 - HAVING ((to_char(INTERVAL '-7 MONS 29 DAYS -0.000000001 SECS', '%X `B') !~* '0SsYa@-p]yc`qTL8PvF #c;Tei9))DXs:^wgv[')) IS NULL + HAVING (false) IS NULL === round=2 query=1 oracle=TlpHavingOracle query_seed=311304 === --- statement=1 context=TLP-HAVING all groups --- SELECT t0.col_t0_2_time64_nanosecond, t0.col_t0_5_timestamp, t0.col_t0_4_interval_month_day_nano FROM t0 - WHERE false + WHERE t0.col_t0_3_boolean GROUP BY t0.col_t0_2_time64_nanosecond, t0.col_t0_5_timestamp, t0.col_t0_4_interval_month_day_nano --- statement=2 context=TLP-HAVING p UNION ALL NOT p UNION ALL p IS NULL --- SELECT t0.col_t0_2_time64_nanosecond, t0.col_t0_5_timestamp, t0.col_t0_4_interval_month_day_nano FROM t0 - WHERE false + WHERE t0.col_t0_3_boolean GROUP BY t0.col_t0_2_time64_nanosecond, t0.col_t0_5_timestamp, t0.col_t0_4_interval_month_day_nano - HAVING (false) + HAVING (('H,0kKL[o[hAzHjO%ac4xA9}vY!/|?5P9' ~~ NULL)) UNION ALL SELECT t0.col_t0_2_time64_nanosecond, t0.col_t0_5_timestamp, t0.col_t0_4_interval_month_day_nano FROM t0 - WHERE false + WHERE t0.col_t0_3_boolean GROUP BY t0.col_t0_2_time64_nanosecond, t0.col_t0_5_timestamp, t0.col_t0_4_interval_month_day_nano - HAVING NOT (false) + HAVING NOT (('H,0kKL[o[hAzHjO%ac4xA9}vY!/|?5P9' ~~ NULL)) UNION ALL SELECT t0.col_t0_2_time64_nanosecond, t0.col_t0_5_timestamp, t0.col_t0_4_interval_month_day_nano FROM t0 - WHERE false + WHERE t0.col_t0_3_boolean GROUP BY t0.col_t0_2_time64_nanosecond, t0.col_t0_5_timestamp, t0.col_t0_4_interval_month_day_nano - HAVING (false) IS NULL + HAVING (('H,0kKL[o[hAzHjO%ac4xA9}vY!/|?5P9' ~~ NULL)) IS NULL + + === round=2 query=2 oracle=TlpHavingOracle query_seed=311305 === + --- statement=1 context=TLP-HAVING all groups --- + SELECT t0.col_t0_3_boolean, t0.col_t0_1_float64 + FROM t0 + WHERE t0.col_t0_3_boolean + GROUP BY t0.col_t0_3_boolean, t0.col_t0_1_float64 + + --- statement=2 context=TLP-HAVING p UNION ALL NOT p UNION ALL p IS NULL --- + SELECT t0.col_t0_3_boolean, t0.col_t0_1_float64 + FROM t0 + WHERE t0.col_t0_3_boolean + GROUP BY t0.col_t0_3_boolean, t0.col_t0_1_float64 + HAVING ((((t0.col_t0_3_boolean OR t0.col_t0_3_boolean) OR ((true = t0.col_t0_3_boolean) OR false)) OR ('1nqYWyq7XW8RrL1i3Y?5^|lH' !~~ 'o}{Fd6Y;LB)7VJ)#"y>Vd:6rQmKB%kV'))) + UNION ALL + SELECT t0.col_t0_3_boolean, t0.col_t0_1_float64 + FROM t0 + WHERE t0.col_t0_3_boolean + GROUP BY t0.col_t0_3_boolean, t0.col_t0_1_float64 + HAVING NOT ((((t0.col_t0_3_boolean OR t0.col_t0_3_boolean) OR ((true = t0.col_t0_3_boolean) OR false)) OR ('1nqYWyq7XW8RrL1i3Y?5^|lH' !~~ 'o}{Fd6Y;LB)7VJ)#"y>Vd:6rQmKB%kV'))) + UNION ALL + SELECT t0.col_t0_3_boolean, t0.col_t0_1_float64 + FROM t0 + WHERE t0.col_t0_3_boolean + GROUP BY t0.col_t0_3_boolean, t0.col_t0_1_float64 + HAVING ((((t0.col_t0_3_boolean OR t0.col_t0_3_boolean) OR ((true = t0.col_t0_3_boolean) OR false)) OR ('1nqYWyq7XW8RrL1i3Y?5^|lH' !~~ 'o}{Fd6Y;LB)7VJ)#"y>Vd:6rQmKB%kV'))) IS NULL === round=2 query=3 oracle=TlpHavingOracle query_seed=311306 === --- statement=1 context=TLP-HAVING all groups --- SELECT t2.col_t2_1_float32 FROM t2 - WHERE false + WHERE (CAST('2055-07-31 17:42:03.799838405 +09:00' AS TIMESTAMP) = CAST('2057-11-21 21:05:28.012190103 +09:30' AS TIMESTAMP)) GROUP BY t2.col_t2_1_float32 --- statement=2 context=TLP-HAVING p UNION ALL NOT p UNION ALL p IS NULL --- SELECT t2.col_t2_1_float32 FROM t2 - WHERE false + WHERE (CAST('2055-07-31 17:42:03.799838405 +09:00' AS TIMESTAMP) = CAST('2057-11-21 21:05:28.012190103 +09:30' AS TIMESTAMP)) GROUP BY t2.col_t2_1_float32 - HAVING (false) + HAVING ((CAST('2028-12-03' AS DATE) <= CAST('2001-11-08' AS DATE))) UNION ALL SELECT t2.col_t2_1_float32 FROM t2 - WHERE false + WHERE (CAST('2055-07-31 17:42:03.799838405 +09:00' AS TIMESTAMP) = CAST('2057-11-21 21:05:28.012190103 +09:30' AS TIMESTAMP)) GROUP BY t2.col_t2_1_float32 - HAVING NOT (false) + HAVING NOT ((CAST('2028-12-03' AS DATE) <= CAST('2001-11-08' AS DATE))) UNION ALL SELECT t2.col_t2_1_float32 FROM t2 - WHERE false + WHERE (CAST('2055-07-31 17:42:03.799838405 +09:00' AS TIMESTAMP) = CAST('2057-11-21 21:05:28.012190103 +09:30' AS TIMESTAMP)) GROUP BY t2.col_t2_1_float32 - HAVING (false) IS NULL + HAVING ((CAST('2028-12-03' AS DATE) <= CAST('2001-11-08' AS DATE))) IS NULL === round=2 query=4 oracle=TlpHavingOracle query_seed=311307 === --- statement=1 context=TLP-HAVING all groups --- @@ -449,17 +519,17 @@ fn full_run_logs_expected_queries_for_tlp_having_oracle() -> Result<(), Box 45)) UNION ALL SELECT t1.col_t1_4_date32 FROM t1 GROUP BY t1.col_t1_4_date32 - HAVING NOT (true) + HAVING NOT ((98 > 45)) UNION ALL SELECT t1.col_t1_4_date32 FROM t1 GROUP BY t1.col_t1_4_date32 - HAVING (true) IS NULL + HAVING ((98 > 45)) IS NULL === round=2 query=5 oracle=TlpHavingOracle query_seed=311308 === --- statement=1 context=TLP-HAVING all groups --- @@ -471,26 +541,26 @@ fn full_run_logs_expected_queries_for_tlp_having_oracle() -> Result<(), Box CAST('2023-08-24 22:24:34.504422016 +09:00' AS TIMESTAMP)))) UNION ALL SELECT t2.col_t2_1_float32 FROM t2 GROUP BY t2.col_t2_1_float32 - HAVING NOT (((82921.0000000000000 + (0.0087828000000000000000000000000000000 % 0.00000000014136000000000000000000000000000000)) < INTERVAL '4 MONS 13 DAYS 0.610363165 SECS')) + HAVING NOT (((CAST('2028-04-15' AS DATE) = CAST('2003-05-12' AS DATE)) AND (NULL <> CAST('2023-08-24 22:24:34.504422016 +09:00' AS TIMESTAMP)))) UNION ALL SELECT t2.col_t2_1_float32 FROM t2 GROUP BY t2.col_t2_1_float32 - HAVING (((82921.0000000000000 + (0.0087828000000000000000000000000000000 % 0.00000000014136000000000000000000000000000000)) < INTERVAL '4 MONS 13 DAYS 0.610363165 SECS')) IS NULL + HAVING (((CAST('2028-04-15' AS DATE) = CAST('2003-05-12' AS DATE)) AND (NULL <> CAST('2023-08-24 22:24:34.504422016 +09:00' AS TIMESTAMP)))) IS NULL "#); - insta::assert_snapshot!(run_output.stats_summary, @" + insta::assert_snapshot!(run_output.stats_summary, @r" ============================================================ 🎯 DataFusion Fuzzer - Final Statistics ============================================================ 📊 Execution Summary: • Rounds Completed: 2 - • Queries Executed: 16 - • Query Success Rate: 81.25% + • Queries Executed: 20 + • Query Success Rate: 100.00% "); fs::remove_dir_all(&log_dir)?;