@@ -2,6 +2,8 @@ use datafusion::arrow::record_batch::RecordBatch;
22use datafusion:: common:: instant:: Instant ;
33use rand:: rngs:: StdRng ;
44use rand:: { Rng , SeedableRng } ;
5+ use std:: fs:: OpenOptions ;
6+ use std:: io:: Write ;
57use std:: sync:: Arc ;
68use std:: time:: Duration ;
79use tracing:: { error, info, warn} ;
@@ -49,7 +51,7 @@ pub async fn run_fuzzer(ctx: Arc<GlobalContext>) -> Result<()> {
4951 let query_seed = query_base_seed. wrapping_add ( i as u64 ) ;
5052
5153 // >>> CORE LOGIC <<<
52- execute_oracle_test ( query_seed, & ctx) . await ;
54+ let _ = execute_oracle_test ( round , i , query_seed, & ctx) . await ? ;
5355 }
5456
5557 update_stat_for_round_completion ( & ctx. fuzzer_stats ) ;
@@ -206,7 +208,12 @@ async fn create_and_register_view(
206208 Ok ( ( ) )
207209}
208210
209- async fn execute_oracle_test ( seed : u64 , ctx : & Arc < GlobalContext > ) -> bool {
211+ async fn execute_oracle_test (
212+ round : u32 ,
213+ query_index : u32 ,
214+ seed : u64 ,
215+ ctx : & Arc < GlobalContext > ,
216+ ) -> Result < bool > {
210217 // Create a deterministic RNG instance for this test
211218 let mut rng = StdRng :: seed_from_u64 ( seed) ;
212219
@@ -229,15 +236,24 @@ async fn execute_oracle_test(seed: u64, ctx: &Arc<GlobalContext>) -> bool {
229236 if !is_error_whitelisted ( & err_msg, None ) {
230237 error ! ( err_msg)
231238 }
232- return false ;
239+ return Ok ( false ) ;
233240 }
234241 } ;
235242
236243 if query_group. is_empty ( ) {
237244 warn ! ( "Oracle generated empty query group" ) ;
238- return false ;
245+ return Ok ( false ) ;
239246 }
240247
248+ append_query_log (
249+ ctx,
250+ round,
251+ query_index,
252+ seed,
253+ selected_oracle. name ( ) ,
254+ & query_group,
255+ ) ?;
256+
241257 // === Execute queries and collect results ===
242258 let mut execution_results = Vec :: new ( ) ;
243259 for query_context in query_group {
@@ -259,7 +275,7 @@ async fn execute_oracle_test(seed: u64, ctx: &Arc<GlobalContext>) -> bool {
259275 {
260276 Ok ( _) => {
261277 info ! ( "Oracle test passed" ) ;
262- true
278+ Ok ( true )
263279 }
264280 Err ( e) => {
265281 error ! ( "Oracle test failed: {}" , e) ;
@@ -268,9 +284,61 @@ async fn execute_oracle_test(seed: u64, ctx: &Arc<GlobalContext>) -> bool {
268284 if let Ok ( error_report) = selected_oracle. create_error_report ( & execution_results) {
269285 error ! ( "Error Report:\n {}" , error_report) ;
270286 }
271- false
287+ Ok ( false )
288+ }
289+ }
290+ }
291+
292+ fn append_query_log (
293+ ctx : & Arc < GlobalContext > ,
294+ round : u32 ,
295+ query_index : u32 ,
296+ query_seed : u64 ,
297+ oracle_name : & str ,
298+ query_group : & [ QueryContext ] ,
299+ ) -> Result < ( ) > {
300+ let Some ( log_dir) = & ctx. runner_config . log_path else {
301+ return Ok ( ( ) ) ;
302+ } ;
303+
304+ let query_log_path = log_dir. join ( "queries.log" ) ;
305+ let mut file = OpenOptions :: new ( )
306+ . create ( true )
307+ . append ( true )
308+ . open ( & query_log_path)
309+ . map_err ( |e| {
310+ crate :: common:: fuzzer_err ( & format ! (
311+ "Failed to open query log '{}': {}" ,
312+ query_log_path. display( ) ,
313+ e
314+ ) )
315+ } ) ?;
316+
317+ writeln ! (
318+ file,
319+ "=== round={} query={} oracle={} query_seed={} ===" ,
320+ round + 1 ,
321+ query_index + 1 ,
322+ oracle_name,
323+ query_seed
324+ ) ?;
325+
326+ for ( statement_index, query_context) in query_group. iter ( ) . enumerate ( ) {
327+ match & query_context. context_description {
328+ Some ( description) => writeln ! (
329+ file,
330+ "--- statement={} context={} ---" ,
331+ statement_index + 1 ,
332+ description
333+ ) ?,
334+ None => writeln ! ( file, "--- statement={} ---" , statement_index + 1 ) ?,
272335 }
336+
337+ writeln ! ( file, "{}" , query_context. query) ?;
338+ writeln ! ( file) ?;
273339 }
340+
341+ Ok ( ( ) )
274342}
275343
276344/// Query execution result that tracks both the outcome and whether it timed out
0 commit comments