@@ -7,6 +7,7 @@ use std::fmt;
77use std:: path:: PathBuf ;
88
99use anyhow:: Result ;
10+ use datafusion:: arrow:: util:: pretty:: pretty_format_batches;
1011use indicatif:: ProgressBar ;
1112use tracing:: warn;
1213use vortex:: error:: VortexExpect ;
@@ -69,10 +70,16 @@ pub struct DriverConfig {
6970 pub skip_generate : bool ,
7071 pub explain : bool ,
7172 pub explain_analyze : bool ,
73+ pub check : bool ,
7274}
7375
7476/// Run a benchmark using the provided implementation and configuration
7577pub fn run_benchmark < B : Benchmark > ( benchmark : B , config : DriverConfig ) -> Result < ( ) > {
78+ // If check mode is enabled, run a single query and print results.
79+ if config. check {
80+ return run_check_query ( benchmark, config) ;
81+ }
82+
7683 // If explain-analyze mode is enabled, run explain analyze
7784 if config. explain_analyze {
7885 return run_explain_query ( benchmark, config, ExplainMode :: Analyze ) ;
@@ -425,3 +432,91 @@ fn run_explain_query<B: Benchmark>(
425432
426433 Ok ( ( ) )
427434}
435+
436+ /// Run a single query and print the results for correctness checking.
437+ fn run_check_query < B : Benchmark > ( benchmark : B , config : DriverConfig ) -> Result < ( ) > {
438+ // Validate exactly one target.
439+ anyhow:: ensure!(
440+ config. targets. len( ) == 1 ,
441+ "--check requires exactly 1 target, but {} were provided" ,
442+ config. targets. len( )
443+ ) ;
444+
445+ // Validate exactly one query is selected.
446+ let Some ( ref queries) = config. queries else {
447+ anyhow:: bail!( "--check requires exactly 1 query to be specified via -q" ) ;
448+ } ;
449+ anyhow:: ensure!(
450+ queries. len( ) == 1 ,
451+ "--check requires exactly 1 query, but {} were specified" ,
452+ queries. len( )
453+ ) ;
454+
455+ let target = & config. targets [ 0 ] ;
456+
457+ // Generate data (idempotent).
458+ if !config. skip_generate {
459+ benchmark. generate_data ( target) ?;
460+ }
461+
462+ let filtered_queries = filter_queries (
463+ benchmark. queries ( ) ?,
464+ config. queries . as_ref ( ) ,
465+ config. exclude_queries . as_ref ( ) ,
466+ ) ;
467+
468+ let tokio_runtime = new_tokio_runtime ( config. threads ) ?;
469+
470+ let engine_ctx = benchmark. setup_engine_context (
471+ target,
472+ config. disable_datafusion_cache ,
473+ config. emit_plan ,
474+ config. delete_duckdb_database ,
475+ config. threads ,
476+ ) ?;
477+
478+ tokio_runtime. block_on ( benchmark. register_tables ( & engine_ctx, target. format ( ) ) ) ?;
479+
480+ for & ( query_idx, ref query_string) in filtered_queries. iter ( ) {
481+ println ! ( "Query {}" , query_idx) ;
482+ println ! ( "SQL: {}\n " , query_string) ;
483+
484+ match & engine_ctx {
485+ EngineCtx :: DataFusion ( ctx) => {
486+ match tokio_runtime. block_on ( ctx. execute_query ( query_string) ) {
487+ Ok ( ( batches, _plan) ) => {
488+ let row_count: usize = batches. iter ( ) . map ( |b| b. num_rows ( ) ) . sum ( ) ;
489+ match pretty_format_batches ( & batches) {
490+ Ok ( formatted) => println ! ( "{}" , formatted) ,
491+ Err ( err) => eprintln ! ( "Error formatting results: {}" , err) ,
492+ }
493+ println ! ( "\n ({} rows)" , row_count) ;
494+ }
495+ Err ( err) => {
496+ eprintln ! ( "Error running query {}: {}" , query_idx, err) ;
497+ }
498+ }
499+ }
500+ EngineCtx :: DuckDB ( ctx) => match ctx. connection . query ( query_string) {
501+ Ok ( result) => {
502+ let mut row_count = 0u64 ;
503+ for chunk in result {
504+ row_count += chunk. len ( ) ;
505+ match String :: try_from ( & chunk) {
506+ Ok ( output) => println ! ( "{}" , output) ,
507+ Err ( err) => {
508+ eprintln ! ( "Error converting chunk to string: {}" , err)
509+ }
510+ }
511+ }
512+ println ! ( "\n ({} rows)" , row_count) ;
513+ }
514+ Err ( err) => {
515+ eprintln ! ( "Error running query {}: {}" , query_idx, err) ;
516+ }
517+ } ,
518+ }
519+ }
520+
521+ Ok ( ( ) )
522+ }
0 commit comments