1414
1515use databend_common_exception:: ErrorCode ;
1616use databend_common_exception:: Result ;
17+ use databend_common_settings:: Settings ;
1718use databend_common_sql:: binder:: is_range_join_condition;
1819use databend_common_sql:: optimizer:: ir:: RelExpr ;
1920use databend_common_sql:: optimizer:: ir:: SExpr ;
@@ -30,17 +31,52 @@ use crate::physical_plans::PhysicalPlanBuilder;
3031enum PhysicalJoinType {
3132 Hash ,
3233 // The first arg is range conditions, the second arg is other conditions
33- RangeJoin ( Vec < ScalarExpr > , Vec < ScalarExpr > ) ,
34+ RangeJoin {
35+ range : Vec < ScalarExpr > ,
36+ other : Vec < ScalarExpr > ,
37+ } ,
38+ LoopJoin {
39+ conditions : Vec < ScalarExpr > ,
40+ } ,
3441}
3542
3643// Choose physical join type by join conditions
37- fn physical_join ( join : & Join , s_expr : & SExpr ) -> Result < PhysicalJoinType > {
44+ fn physical_join ( join : & Join , s_expr : & SExpr , settings : & Settings ) -> Result < PhysicalJoinType > {
3845 if join. equi_conditions . is_empty ( ) && join. join_type . is_any_join ( ) {
3946 return Err ( ErrorCode :: SemanticError (
4047 "ANY JOIN only supports equality-based hash joins" ,
4148 ) ) ;
4249 }
4350
51+ let left_rel_expr = RelExpr :: with_s_expr ( s_expr. left_child ( ) ) ;
52+ let right_rel_expr = RelExpr :: with_s_expr ( s_expr. right_child ( ) ) ;
53+ let right_stat_info = right_rel_expr. derive_cardinality ( ) ?;
54+ let nested_loop_join_threshold = settings. get_nested_loop_join_threshold ( ) ?;
55+ if matches ! ( join. join_type, JoinType :: Inner | JoinType :: Cross )
56+ && ( right_stat_info
57+ . statistics
58+ . precise_cardinality
59+ . map ( |n| n < nested_loop_join_threshold)
60+ . unwrap_or ( false )
61+ || right_stat_info. cardinality < nested_loop_join_threshold as _ )
62+ {
63+ let conditions = join
64+ . non_equi_conditions
65+ . iter ( )
66+ . cloned ( )
67+ . chain ( join. equi_conditions . iter ( ) . cloned ( ) . map ( |condition| {
68+ FunctionCall {
69+ span : condition. left . span ( ) ,
70+ func_name : "eq" . to_string ( ) ,
71+ params : vec ! [ ] ,
72+ arguments : vec ! [ condition. left, condition. right] ,
73+ }
74+ . into ( )
75+ } ) )
76+ . collect ( ) ;
77+ return Ok ( PhysicalJoinType :: LoopJoin { conditions } ) ;
78+ } ;
79+
4480 if !join. equi_conditions . is_empty ( ) {
4581 // Contain equi condition, use hash join
4682 return Ok ( PhysicalJoinType :: Hash ) ;
@@ -51,32 +87,29 @@ fn physical_join(join: &Join, s_expr: &SExpr) -> Result<PhysicalJoinType> {
5187 return Ok ( PhysicalJoinType :: Hash ) ;
5288 }
5389
54- let left_rel_expr = RelExpr :: with_s_expr ( s_expr. child ( 0 ) ?) ;
55- let right_rel_expr = RelExpr :: with_s_expr ( s_expr. child ( 1 ) ?) ;
56- let right_stat_info = right_rel_expr. derive_cardinality ( ) ?;
5790 if matches ! ( right_stat_info. statistics. precise_cardinality, Some ( 1 ) )
5891 || right_stat_info. cardinality == 1.0
5992 {
6093 // If the output rows of build side is equal to 1, we use CROSS JOIN + FILTER instead of RANGE JOIN.
6194 return Ok ( PhysicalJoinType :: Hash ) ;
6295 }
6396
64- let left_prop = left_rel_expr. derive_relational_prop ( ) ?;
65- let right_prop = right_rel_expr. derive_relational_prop ( ) ?;
66- let ( range_conditions, other_conditions) = join
67- . non_equi_conditions
68- . iter ( )
69- . cloned ( )
70- . partition :: < Vec < _ > , _ > ( |condition| {
71- is_range_join_condition ( condition, & left_prop, & right_prop) . is_some ( )
72- } ) ;
73-
74- if !range_conditions. is_empty ( ) && matches ! ( join. join_type, JoinType :: Inner | JoinType :: Cross ) {
75- return Ok ( PhysicalJoinType :: RangeJoin (
76- range_conditions,
77- other_conditions,
78- ) ) ;
97+ if matches ! ( join. join_type, JoinType :: Inner | JoinType :: Cross ) {
98+ let left_prop = left_rel_expr. derive_relational_prop ( ) ?;
99+ let right_prop = right_rel_expr. derive_relational_prop ( ) ?;
100+ let ( range, other) = join
101+ . non_equi_conditions
102+ . iter ( )
103+ . cloned ( )
104+ . partition :: < Vec < _ > , _ > ( |condition| {
105+ is_range_join_condition ( condition, & left_prop, & right_prop) . is_some ( )
106+ } ) ;
107+
108+ if !range. is_empty ( ) {
109+ return Ok ( PhysicalJoinType :: RangeJoin { range, other } ) ;
110+ }
79111 }
112+
80113 // Leverage hash join to execute nested loop join
81114 Ok ( PhysicalJoinType :: Hash )
82115}
@@ -157,7 +190,8 @@ impl PhysicalPlanBuilder {
157190 )
158191 . await
159192 } else {
160- match physical_join ( join, s_expr) ? {
193+ let settings = self . ctx . get_settings ( ) ;
194+ match physical_join ( join, s_expr, & settings) ? {
161195 PhysicalJoinType :: Hash => {
162196 self . build_hash_join (
163197 join,
@@ -170,7 +204,7 @@ impl PhysicalPlanBuilder {
170204 )
171205 . await
172206 }
173- PhysicalJoinType :: RangeJoin ( range, other) => {
207+ PhysicalJoinType :: RangeJoin { range, other } => {
174208 self . build_range_join (
175209 join. join_type ,
176210 s_expr,
@@ -181,6 +215,16 @@ impl PhysicalPlanBuilder {
181215 )
182216 . await
183217 }
218+ PhysicalJoinType :: LoopJoin { conditions } => {
219+ self . build_loop_join (
220+ join. join_type ,
221+ s_expr,
222+ left_required,
223+ right_required,
224+ conditions,
225+ )
226+ . await
227+ }
184228 }
185229 }
186230 }
0 commit comments