11use crate :: common:: { Result , fuzzer_err, util} ;
22use crate :: oracle:: QueryExecutionResult ;
33
4+ pub ( crate ) fn validate_binary_tlp_consistency (
5+ results : & [ QueryExecutionResult ] ,
6+ oracle_name : & str ,
7+ ) -> Result < ( ) > {
8+ let result_count = results. len ( ) ;
9+ if result_count != 2 {
10+ return Err ( fuzzer_err ( & format ! (
11+ "{oracle_name} expects 2 query results, got {result_count}"
12+ ) ) ) ;
13+ }
14+
15+ let num_ok = results. iter ( ) . filter ( |r| r. result . is_ok ( ) ) . count ( ) ;
16+ let num_err = result_count - num_ok;
17+
18+ match num_ok {
19+ 2 => validate_value_equivalence ( results, 0 , 1 , oracle_name) ,
20+ 0 => Ok ( ( ) ) ,
21+ _ => Err ( fuzzer_err ( & format ! (
22+ "{oracle_name} consistency requires all queries to either succeed or fail; got mixed outcomes (ok={num_ok}, err={num_err})"
23+ ) ) ) ,
24+ }
25+ }
26+
427pub ( crate ) fn validate_value_equivalence (
528 results : & [ QueryExecutionResult ] ,
629 left_idx : usize ,
@@ -25,3 +48,67 @@ pub(crate) fn validate_value_equivalence(
2548
2649 util:: validate_batches_value_equivalence ( left_batches, right_batches, oracle_name)
2750}
51+
52+ pub ( crate ) fn append_labeled_query_results (
53+ report : & mut String ,
54+ results : & [ QueryExecutionResult ] ,
55+ labels : & [ & str ] ,
56+ ) {
57+ for ( idx, result) in results. iter ( ) . enumerate ( ) {
58+ let label = labels. get ( idx) . copied ( ) . unwrap_or ( "unknown" ) ;
59+ report. push_str ( & format ! (
60+ "Q{} [{}]:\n {}\n " ,
61+ idx + 1 ,
62+ label,
63+ result. query_context. query
64+ ) ) ;
65+
66+ match & result. result {
67+ Ok ( batches) => report. push_str ( & format ! (
68+ " status: ok, rows={}\n \n " ,
69+ util:: count_total_rows( batches)
70+ ) ) ,
71+ Err ( e) => report. push_str ( & format ! ( " status: error, details={}\n \n " , e) ) ,
72+ }
73+ }
74+ }
75+
76+ pub ( crate ) fn append_binary_value_equivalence_report (
77+ report : & mut String ,
78+ results : & [ QueryExecutionResult ] ,
79+ ) -> Result < ( ) > {
80+ if results. len ( ) != 2 || results. iter ( ) . any ( |r| r. result . is_err ( ) ) {
81+ return Ok ( ( ) ) ;
82+ }
83+
84+ let q_all_batches = results[ 0 ]
85+ . result
86+ . as_ref ( )
87+ . map_err ( |e| fuzzer_err ( & e. to_string ( ) ) ) ?;
88+ let q_union_batches = results[ 1 ]
89+ . result
90+ . as_ref ( )
91+ . map_err ( |e| fuzzer_err ( & e. to_string ( ) ) ) ?;
92+
93+ report. push_str ( & format ! (
94+ "Row counts: all={}, partition_union={}\n " ,
95+ util:: count_total_rows( q_all_batches) ,
96+ util:: count_total_rows( q_union_batches)
97+ ) ) ;
98+
99+ let all_multiset = util:: batches_to_row_multiset ( q_all_batches) ?;
100+ let partition_multiset = util:: batches_to_row_multiset ( q_union_batches) ?;
101+
102+ if all_multiset != partition_multiset {
103+ report. push_str ( "\n Top multiset differences:\n " ) ;
104+ report. push_str ( & util:: format_row_multiset_diff (
105+ & all_multiset,
106+ & partition_multiset,
107+ ) ) ;
108+ report. push ( '\n' ) ;
109+ } else {
110+ report. push_str ( "Multiset equivalence: true\n " ) ;
111+ }
112+
113+ Ok ( ( ) )
114+ }
0 commit comments