1515// specific language governing permissions and limitations
1616// under the License.
1717
18- use crate :: logical_plan:: producer:: { SubstraitProducer , make_binary_op_scalar_func} ;
19- use datafusion:: common:: {
20- DFSchemaRef , JoinConstraint , JoinType , NullEquality , not_impl_err,
21- } ;
18+ use crate :: logical_plan:: producer:: SubstraitProducer ;
19+ use datafusion:: common:: { JoinConstraint , JoinType , NullEquality , not_impl_err} ;
20+ use datafusion:: logical_expr:: utils:: conjunction;
2221use datafusion:: logical_expr:: { Expr , Join , Operator } ;
22+ use datafusion:: prelude:: binary_expr;
2323use std:: sync:: Arc ;
2424use substrait:: proto:: rel:: RelType ;
25- use substrait:: proto:: { Expression , JoinRel , Rel , join_rel} ;
25+ use substrait:: proto:: { JoinRel , Rel , join_rel} ;
2626
2727pub fn from_join (
2828 producer : & mut impl SubstraitProducer ,
2929 join : & Join ,
3030) -> datafusion:: common:: Result < Box < Rel > > {
31- let left = producer. handle_plan ( join. left . as_ref ( ) ) ?;
32- let right = producer. handle_plan ( join. right . as_ref ( ) ) ?;
33- let join_type = to_substrait_jointype ( join. join_type ) ;
34- // we only support basic joins so return an error for anything not yet supported
31+ // only ON constraints are supported right now
3532 match join. join_constraint {
3633 JoinConstraint :: On => { }
3734 JoinConstraint :: Using => return not_impl_err ! ( "join constraint: `using`" ) ,
3835 }
39- let in_join_schema = Arc :: new ( join. left . schema ( ) . join ( join. right . schema ( ) ) ?) ;
40-
41- // convert filter if present
42- let join_filter = match & join. filter {
43- Some ( filter) => Some ( producer. handle_expr ( filter, & in_join_schema) ?) ,
44- None => None ,
45- } ;
4636
47- // map the left and right columns to binary expressions in the form `l = r`
48- // build a single expression for the ON condition, such as `l.a = r.a AND l.b = r.b`
49- let eq_op = match join. null_equality {
50- NullEquality :: NullEqualsNothing => Operator :: Eq ,
51- NullEquality :: NullEqualsNull => Operator :: IsNotDistinctFrom ,
52- } ;
53- let join_on = to_substrait_join_expr ( producer, & join. on , eq_op, & in_join_schema) ?;
37+ let left = producer. handle_plan ( join. left . as_ref ( ) ) ?;
38+ let right = producer. handle_plan ( join. right . as_ref ( ) ) ?;
39+ let join_type = to_substrait_jointype ( join. join_type ) ;
5440
55- // create conjunction between `join_on` and `join_filter` to embed all join conditions,
56- // whether equal or non-equal in a single expression
57- let join_expr = match & join_on {
58- Some ( on_expr) => match & join_filter {
59- Some ( filter) => Some ( Box :: new ( make_binary_op_scalar_func (
60- producer,
61- on_expr,
62- filter,
63- Operator :: And ,
64- ) ) ) ,
65- None => join_on. map ( Box :: new) , // the join expression will only contain `join_on` if filter doesn't exist
66- } ,
67- None => match & join_filter {
68- Some ( _) => join_filter. map ( Box :: new) , // the join expression will only contain `join_filter` if the `on` condition doesn't exist
69- None => None ,
70- } ,
41+ let join_expr =
42+ to_substrait_join_expr ( join. on . clone ( ) , join. null_equality , join. filter . clone ( ) ) ;
43+ let join_expression = match join_expr {
44+ Some ( expr) => {
45+ let in_join_schema = Arc :: new ( join. left . schema ( ) . join ( join. right . schema ( ) ) ?) ;
46+ let expression = producer. handle_expr ( & expr, & in_join_schema) ?;
47+ Some ( Box :: new ( expression) )
48+ }
49+ None => None ,
7150 } ;
7251
7352 Ok ( Box :: new ( Rel {
@@ -76,33 +55,28 @@ pub fn from_join(
7655 left : Some ( left) ,
7756 right : Some ( right) ,
7857 r#type : join_type as i32 ,
79- expression : join_expr ,
58+ expression : join_expression ,
8059 post_join_filter : None ,
8160 advanced_extension : None ,
8261 } ) ) ) ,
8362 } ) )
8463}
8564
8665fn to_substrait_join_expr (
87- producer : & mut impl SubstraitProducer ,
88- join_conditions : & Vec < ( Expr , Expr ) > ,
89- eq_op : Operator ,
90- join_schema : & DFSchemaRef ,
91- ) -> datafusion:: common:: Result < Option < Expression > > {
92- // Only support AND conjunction for each binary expression in join conditions
93- let mut exprs: Vec < Expression > = vec ! [ ] ;
94- for ( left, right) in join_conditions {
95- let l = producer. handle_expr ( left, join_schema) ?;
96- let r = producer. handle_expr ( right, join_schema) ?;
97- // AND with existing expression
98- exprs. push ( make_binary_op_scalar_func ( producer, & l, & r, eq_op) ) ;
99- }
100-
101- let join_expr: Option < Expression > =
102- exprs. into_iter ( ) . reduce ( |acc : Expression , e : Expression | {
103- make_binary_op_scalar_func ( producer, & acc, & e, Operator :: And )
104- } ) ;
105- Ok ( join_expr)
66+ join_on : Vec < ( Expr , Expr ) > ,
67+ null_equality : NullEquality ,
68+ join_filter : Option < Expr > ,
69+ ) -> Option < Expr > {
70+ // Combine join on and filter conditions into a single Boolean expression (#7611)
71+ let eq_op = match null_equality {
72+ NullEquality :: NullEqualsNothing => Operator :: Eq ,
73+ NullEquality :: NullEqualsNull => Operator :: IsNotDistinctFrom ,
74+ } ;
75+ let all_conditions = join_on
76+ . into_iter ( )
77+ . map ( |( left, right) | binary_expr ( left, eq_op, right) )
78+ . chain ( join_filter) ;
79+ conjunction ( all_conditions)
10680}
10781
10882fn to_substrait_jointype ( join_type : JoinType ) -> join_rel:: JoinType {
0 commit comments