1717
1818//! Utility functions leveraged by the query optimizer rules
1919
20- mod null_restriction;
21-
2220use std:: collections:: { BTreeSet , HashMap , HashSet } ;
2321use std:: sync:: Arc ;
2422
@@ -38,12 +36,6 @@ use log::{debug, trace};
3836/// as it was initially placed here and then moved elsewhere.
3937pub use datafusion_expr:: expr_rewriter:: NamePreserver ;
4038
41- #[ cfg( test) ]
42- use self :: test_eval_mode:: {
43- NullRestrictionEvalMode , null_restriction_eval_mode,
44- set_null_restriction_eval_mode_for_test, with_null_restriction_eval_mode_for_test,
45- } ;
46-
4739/// Returns true if `expr` contains all columns in `schema_cols`
4840pub ( crate ) fn has_all_column_refs ( expr : & Expr , schema_cols : & HashSet < Column > ) -> bool {
4941 expr. column_refs ( )
@@ -86,40 +78,17 @@ pub fn is_restrict_null_predicate<'a>(
8678 // Collect join columns so they can be used in both the fast-path check and the
8779 // fallback evaluation path below.
8880 let join_cols: HashSet < & Column > = join_cols_of_predicate. into_iter ( ) . collect ( ) ;
89- let column_refs = predicate. column_refs ( ) ;
90-
9181 // Fast path: if the predicate references columns outside the join key set,
9282 // `evaluate_expr_with_null_column` would fail because the null schema only
9383 // contains a placeholder for the join key columns. Callers treat such errors as
9484 // non-restricting (false) via `matches!(_, Ok(true))`, so we return false early
9585 // and avoid the expensive physical-expression compilation pipeline entirely.
96- if !column_refs. iter ( ) . all ( |column| join_cols. contains ( * column) ) {
97- return Ok ( false ) ;
98- }
99-
100- #[ cfg( test) ]
101- if matches ! (
102- null_restriction_eval_mode( ) ,
103- NullRestrictionEvalMode :: AuthoritativeOnly
104- ) {
105- return authoritative_restrict_null_predicate ( predicate, join_cols) ;
106- }
107-
108- if let Some ( is_restricting) =
109- null_restriction:: syntactic_restrict_null_predicate ( & predicate, & join_cols)
86+ if !predicate
87+ . column_refs ( )
88+ . iter ( )
89+ . all ( |column| join_cols. contains ( * column) )
11090 {
111- #[ cfg( debug_assertions) ]
112- {
113- let authoritative = authoritative_restrict_null_predicate (
114- predicate. clone ( ) ,
115- join_cols. iter ( ) . copied ( ) ,
116- ) ?;
117- debug_assert_eq ! (
118- is_restricting, authoritative,
119- "syntactic fast path disagrees with authoritative null-restriction evaluation for predicate: {predicate}"
120- ) ;
121- }
122- return Ok ( is_restricting) ;
91+ return Ok ( false ) ;
12392 }
12493
12594 authoritative_restrict_null_predicate ( predicate, join_cols)
@@ -198,75 +167,13 @@ fn coerce(expr: Expr, schema: &DFSchema) -> Result<Expr> {
198167 expr. rewrite ( & mut expr_rewrite) . data ( )
199168}
200169
201- #[ cfg( test) ]
202- mod test_eval_mode {
203- use std:: cell:: Cell ;
204-
205- /// Null restriction evaluation mode for optimizer tests.
206- #[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
207- pub ( crate ) enum NullRestrictionEvalMode {
208- Auto ,
209- AuthoritativeOnly ,
210- }
211-
212- thread_local ! {
213- static NULL_RESTRICTION_EVAL_MODE : Cell <NullRestrictionEvalMode > =
214- const { Cell :: new( NullRestrictionEvalMode :: Auto ) } ;
215- }
216-
217- pub ( crate ) fn set_null_restriction_eval_mode_for_test ( mode : NullRestrictionEvalMode ) {
218- NULL_RESTRICTION_EVAL_MODE . with ( |eval_mode| eval_mode. set ( mode) ) ;
219- }
220-
221- pub ( crate ) fn null_restriction_eval_mode ( ) -> NullRestrictionEvalMode {
222- NULL_RESTRICTION_EVAL_MODE . with ( Cell :: get)
223- }
224-
225- pub ( crate ) fn with_null_restriction_eval_mode_for_test < T > (
226- mode : NullRestrictionEvalMode ,
227- f : impl FnOnce ( ) -> T ,
228- ) -> T {
229- struct NullRestrictionEvalModeReset ( NullRestrictionEvalMode ) ;
230-
231- impl Drop for NullRestrictionEvalModeReset {
232- fn drop ( & mut self ) {
233- set_null_restriction_eval_mode_for_test ( self . 0 ) ;
234- }
235- }
236-
237- let previous_mode = null_restriction_eval_mode ( ) ;
238- set_null_restriction_eval_mode_for_test ( mode) ;
239- let _reset = NullRestrictionEvalModeReset ( previous_mode) ;
240- f ( )
241- }
242- }
243-
244170#[ cfg( test) ]
245171mod tests {
246172 use super :: * ;
247- use std:: panic:: { AssertUnwindSafe , catch_unwind} ;
248-
249173 use datafusion_expr:: {
250174 Operator , binary_expr, case, col, in_list, is_null, lit, when,
251175 } ;
252176
253- fn restrict_null_predicate_in_modes (
254- predicate : Expr ,
255- join_cols : & [ Column ] ,
256- ) -> Result < ( bool , bool ) > {
257- let auto_result = with_null_restriction_eval_mode_for_test (
258- NullRestrictionEvalMode :: Auto ,
259- || is_restrict_null_predicate ( predicate. clone ( ) , join_cols. iter ( ) ) ,
260- ) ?;
261-
262- let authoritative_result = with_null_restriction_eval_mode_for_test (
263- NullRestrictionEvalMode :: AuthoritativeOnly ,
264- || is_restrict_null_predicate ( predicate. clone ( ) , join_cols. iter ( ) ) ,
265- ) ?;
266-
267- Ok ( ( auto_result, authoritative_result) )
268- }
269-
270177 #[ test]
271178 fn expr_is_restrict_null_predicate ( ) -> Result < ( ) > {
272179 let test_cases = vec ! [
@@ -409,118 +316,4 @@ mod tests {
409316
410317 Ok ( ( ) )
411318 }
412-
413- #[ test]
414- fn syntactic_fast_path_matches_authoritative_evaluator ( ) -> Result < ( ) > {
415- let test_cases = vec ! [
416- is_null( col( "a" ) ) ,
417- Expr :: IsNotNull ( Box :: new( col( "a" ) ) ) ,
418- binary_expr( col( "a" ) , Operator :: Gt , lit( 8i64 ) ) ,
419- binary_expr( col( "a" ) , Operator :: Eq , lit( ScalarValue :: Null ) ) ,
420- binary_expr( col( "a" ) , Operator :: And , lit( true ) ) ,
421- binary_expr( col( "a" ) , Operator :: Or , lit( false ) ) ,
422- Expr :: Not ( Box :: new( col( "a" ) . is_true( ) ) ) ,
423- col( "a" ) . is_true( ) ,
424- col( "a" ) . is_false( ) ,
425- col( "a" ) . is_unknown( ) ,
426- col( "a" ) . is_not_true( ) ,
427- col( "a" ) . is_not_false( ) ,
428- col( "a" ) . is_not_unknown( ) ,
429- col( "a" ) . between( lit( 1i64 ) , lit( 10i64 ) ) ,
430- binary_expr(
431- when( Expr :: IsNotNull ( Box :: new( col( "a" ) ) ) , col( "a" ) )
432- . otherwise( col( "b" ) ) ?,
433- Operator :: Gt ,
434- lit( 2i64 ) ,
435- ) ,
436- case( col( "a" ) )
437- . when( lit( 1i64 ) , lit( true ) )
438- . otherwise( lit( false ) ) ?,
439- case( col( "a" ) )
440- . when( lit( 0i64 ) , lit( false ) )
441- . otherwise( lit( true ) ) ?,
442- binary_expr(
443- case( col( "a" ) )
444- . when( lit( 0i64 ) , lit( true ) )
445- . otherwise( lit( false ) ) ?,
446- Operator :: Or ,
447- lit( false ) ,
448- ) ,
449- binary_expr(
450- case( lit( 1i64 ) )
451- . when( lit( 1i64 ) , lit( ScalarValue :: Null ) )
452- . otherwise( lit( false ) ) ?,
453- Operator :: IsNotDistinctFrom ,
454- lit( true ) ,
455- ) ,
456- ] ;
457-
458- for predicate in test_cases {
459- let join_cols = predicate. column_refs ( ) ;
460- if let Some ( syntactic) = null_restriction:: syntactic_restrict_null_predicate (
461- & predicate, & join_cols,
462- ) {
463- let authoritative = authoritative_restrict_null_predicate (
464- predicate. clone ( ) ,
465- join_cols. iter ( ) . copied ( ) ,
466- )
467- . unwrap_or_else ( |error| {
468- panic ! (
469- "authoritative evaluator failed for predicate `{predicate}`: {error}"
470- )
471- } ) ;
472- assert_eq ! (
473- syntactic, authoritative,
474- "syntactic fast path disagrees with authoritative evaluator for predicate: {predicate}" ,
475- ) ;
476- }
477- }
478-
479- Ok ( ( ) )
480- }
481-
482- #[ test]
483- fn null_restriction_eval_mode_auto_vs_authoritative_only ( ) -> Result < ( ) > {
484- let predicate = binary_expr ( col ( "a" ) , Operator :: Gt , lit ( 8i64 ) ) ;
485- let join_cols_of_predicate = predicate
486- . column_refs ( )
487- . into_iter ( )
488- . cloned ( )
489- . collect :: < Vec < _ > > ( ) ;
490- let ( auto_result, authoritative_result) =
491- restrict_null_predicate_in_modes ( predicate, & join_cols_of_predicate) ?;
492-
493- assert_eq ! ( auto_result, authoritative_result) ;
494-
495- Ok ( ( ) )
496- }
497-
498- #[ test]
499- fn mixed_reference_predicate_remains_fast_pathed_in_authoritative_mode ( ) -> Result < ( ) >
500- {
501- let predicate = binary_expr ( col ( "a" ) , Operator :: Gt , col ( "b" ) ) ;
502- let join_cols = vec ! [ Column :: from_name( "a" ) ] ;
503- let ( auto_result, authoritative_only_result) =
504- restrict_null_predicate_in_modes ( predicate. clone ( ) , & join_cols) ?;
505-
506- assert ! ( !auto_result, "{predicate}" ) ;
507- assert ! ( !authoritative_only_result, "{predicate}" ) ;
508-
509- Ok ( ( ) )
510- }
511-
512- #[ test]
513- fn null_restriction_eval_mode_guard_restores_on_panic ( ) {
514- set_null_restriction_eval_mode_for_test ( NullRestrictionEvalMode :: Auto ) ;
515-
516- let result = catch_unwind ( AssertUnwindSafe ( || {
517- with_null_restriction_eval_mode_for_test (
518- NullRestrictionEvalMode :: AuthoritativeOnly ,
519- || panic ! ( "intentional panic to verify test mode reset" ) ,
520- )
521- } ) ) ;
522-
523- assert ! ( result. is_err( ) ) ;
524- assert_eq ! ( null_restriction_eval_mode( ) , NullRestrictionEvalMode :: Auto ) ;
525- }
526319}
0 commit comments