From 5360b8966efd7cde61f6f9978c3ccc60290d946e Mon Sep 17 00:00:00 2001 From: Alex Chi Date: Thu, 7 Nov 2024 17:46:36 -0500 Subject: [PATCH] fix(df-repr): join assoc rule expr, rm exploding rules (#223) Signed-off-by: Alex Chi --- optd-datafusion-repr/src/lib.rs | 7 +- optd-datafusion-repr/src/rules/joins.rs | 57 +---- .../tests/basic/cross_product.planner.sql | 2 +- .../tests/basic/filter.planner.sql | 6 +- .../tests/joins/join_enumerate.planner.sql | 30 ++- .../tests/joins/join_enumerate.yml | 17 +- .../tests/joins/multi-join.planner.sql | 92 ++++++++ .../tests/joins/multi-join.yml | 22 ++ .../subqueries/subquery_unnesting.planner.sql | 32 +-- .../tests/tpch/tpch-01-05.planner.sql | 184 +++++++-------- .../tests/tpch/tpch-06-10.planner.sql | 210 ++++++------------ .../tests/tpch/tpch-11-15.planner.sql | 18 +- .../tests/tpch/tpch-16-20.planner.sql | 2 +- 13 files changed, 338 insertions(+), 341 deletions(-) create mode 100644 optd-sqlplannertest/tests/joins/multi-join.planner.sql create mode 100644 optd-sqlplannertest/tests/joins/multi-join.yml diff --git a/optd-datafusion-repr/src/lib.rs b/optd-datafusion-repr/src/lib.rs index bff15a31..d30384ba 100644 --- a/optd-datafusion-repr/src/lib.rs +++ b/optd-datafusion-repr/src/lib.rs @@ -121,15 +121,9 @@ impl DatafusionOptimizer { rule_wrappers.push(RuleWrapper::new_cascades(Arc::new( rules::JoinCommuteRule::new(), ))); - rule_wrappers.push(RuleWrapper::new_cascades(Arc::new( - rules::InnerCrossJoinRule::new(), - ))); rule_wrappers.push(RuleWrapper::new_cascades(Arc::new( rules::JoinAssocRule::new(), ))); - rule_wrappers.push(RuleWrapper::new_cascades(Arc::new( - rules::JoinAbsorbFilterRule::new(), - ))); rule_wrappers.push(RuleWrapper::new_cascades(Arc::new( rules::ProjectionPullUpJoin::new(), ))); @@ -186,6 +180,7 @@ impl DatafusionOptimizer { panic_on_budget: false, partial_explore_iter: Some(1 << 20), partial_explore_space: Some(1 << 10), + disable_pruning: false, }, ), heuristic_optimizer: HeuristicsOptimizer::new_with_rules( diff --git a/optd-datafusion-repr/src/rules/joins.rs b/optd-datafusion-repr/src/rules/joins.rs index 8a872253..d8355e2a 100644 --- a/optd-datafusion-repr/src/rules/joins.rs +++ b/optd-datafusion-repr/src/rules/joins.rs @@ -8,52 +8,11 @@ use super::macros::{define_impl_rule, define_rule}; use crate::plan_nodes::{ ArcDfPlanNode, BinOpPred, BinOpType, ColumnRefPred, ConstantPred, ConstantType, DfNodeType, DfPredType, DfReprPlanNode, DfReprPredNode, JoinType, ListPred, LogOpType, - LogicalEmptyRelation, LogicalFilter, LogicalJoin, LogicalProjection, PhysicalHashJoin, PredExt, + LogicalEmptyRelation, LogicalJoin, LogicalProjection, PhysicalHashJoin, PredExt, }; use crate::properties::schema::Schema; use crate::OptimizerExt; -// A cross join B -> A inner join B -define_rule!( - InnerCrossJoinRule, - apply_inner_cross_join, - (Join(JoinType::Cross), left, right) -); - -fn apply_inner_cross_join( - _: &impl Optimizer, - binding: ArcDfPlanNode, -) -> Vec> { - let join = LogicalJoin::from_plan_node(binding).unwrap(); - let node = LogicalJoin::new_unchecked(join.left(), join.right(), join.cond(), JoinType::Inner); - vec![node.into_plan_node().into()] -} - -// Filter (A inner join B on true) cond -> A inner join B on cond -define_rule!( - JoinAbsorbFilterRule, - apply_join_absorb_filter, - (Filter, (Join(JoinType::Inner), left, right)) -); - -fn apply_join_absorb_filter( - _: &impl Optimizer, - binding: ArcDfPlanNode, -) -> Vec> { - let filter = LogicalFilter::from_plan_node(binding).unwrap(); - let join = LogicalJoin::from_plan_node(filter.child().unwrap_plan_node()).unwrap(); - let join_cond = join.cond(); - let filter_cond = filter.cond(); - if let Some(constant) = ConstantPred::from_pred_node(join_cond) { - if constant.value().as_bool() { - let node = - LogicalJoin::new_unchecked(join.left(), join.right(), filter_cond, JoinType::Inner); - return vec![node.into_plan_node().into()]; - } - } - vec![] -} - // A join B -> B join A define_rule!( JoinCommuteRule, @@ -112,7 +71,15 @@ fn apply_eliminate_join( if let DfPredType::Constant(const_type) = cond.typ { if const_type == ConstantType::Bool { if let Some(ref data) = cond.data { - if !data.as_bool() { + if data.as_bool() { + let node = LogicalJoin::new_unchecked( + left, + right, + ConstantPred::bool(true).into_pred_node(), + JoinType::Cross, + ); + return vec![node.into_plan_node().into()]; + } else { // No need to handle schema here, as all exprs in the same group // will have same logical properties let mut left_fields = optimizer.get_schema_of(left.clone()).fields; @@ -146,9 +113,9 @@ fn apply_join_assoc( let join2 = LogicalJoin::from_plan_node(join1.left().unwrap_plan_node()).unwrap(); let a = join2.left(); let b = join2.right(); - let cond2 = join2.cond(); + let cond1 = join2.cond(); let a_schema = optimizer.get_schema_of(a.clone()); - let cond1 = join1.cond(); + let cond2 = join1.cond(); let Some(cond2) = cond2.rewrite_column_refs(&mut |idx| { if idx < a_schema.len() { diff --git a/optd-sqlplannertest/tests/basic/cross_product.planner.sql b/optd-sqlplannertest/tests/basic/cross_product.planner.sql index bb0e112a..0799cc5a 100644 --- a/optd-sqlplannertest/tests/basic/cross_product.planner.sql +++ b/optd-sqlplannertest/tests/basic/cross_product.planner.sql @@ -17,7 +17,7 @@ LogicalProjection { exprs: [ #0, #1 ] } └── LogicalJoin { join_type: Cross, cond: true } ├── LogicalScan { table: t1 } └── LogicalScan { table: t2 } -PhysicalNestedLoopJoin { join_type: Inner, cond: true } +PhysicalNestedLoopJoin { join_type: Cross, cond: true } ├── PhysicalScan { table: t1 } └── PhysicalScan { table: t2 } 0 0 diff --git a/optd-sqlplannertest/tests/basic/filter.planner.sql b/optd-sqlplannertest/tests/basic/filter.planner.sql index 0e2ba398..f3ad2f1c 100644 --- a/optd-sqlplannertest/tests/basic/filter.planner.sql +++ b/optd-sqlplannertest/tests/basic/filter.planner.sql @@ -97,7 +97,7 @@ PhysicalFilter │ └── Eq │ ├── #0 │ └── #3 -└── PhysicalNestedLoopJoin { join_type: Inner, cond: true } +└── PhysicalNestedLoopJoin { join_type: Cross, cond: true } ├── PhysicalScan { table: t1 } └── PhysicalScan { table: t2 } 0 0 0 200 @@ -122,7 +122,7 @@ LogicalProjection { exprs: [ #0, #1, #2, #3 ] } └── LogicalJoin { join_type: Cross, cond: true } ├── LogicalScan { table: t1 } └── LogicalScan { table: t2 } -PhysicalNestedLoopJoin { join_type: Inner, cond: true } +PhysicalNestedLoopJoin { join_type: Cross, cond: true } ├── PhysicalScan { table: t1 } └── PhysicalScan { table: t2 } 0 0 0 200 @@ -254,7 +254,7 @@ LogicalProjection { exprs: [ #0, #1, #2, #3 ] } │ └── true ├── LogicalScan { table: t1 } └── LogicalScan { table: t2 } -PhysicalNestedLoopJoin { join_type: Inner, cond: true } +PhysicalNestedLoopJoin { join_type: Cross, cond: true } ├── PhysicalScan { table: t1 } └── PhysicalScan { table: t2 } 0 0 0 200 diff --git a/optd-sqlplannertest/tests/joins/join_enumerate.planner.sql b/optd-sqlplannertest/tests/joins/join_enumerate.planner.sql index 41d91ab8..d4ae391a 100644 --- a/optd-sqlplannertest/tests/joins/join_enumerate.planner.sql +++ b/optd-sqlplannertest/tests/joins/join_enumerate.planner.sql @@ -24,12 +24,28 @@ select * from t2, t1 where t1v1 = t2v1; 2 202 2 2 */ --- Test whether the optimizer enumerates all 3-join orders. +-- Test whether the optimizer enumerates all 3-join orders. (It should) +select * from t2, t1, t3 where t1v1 = t2v1 and t1v1 = t3v2; + +/* +(Join t2 (Join t1 t3)) +(Join t2 (Join t3 t1)) +(Join t3 (Join t1 t2)) +(Join t3 (Join t2 t1)) +(Join (Join t1 t2) t3) +(Join (Join t1 t3) t2) +(Join (Join t2 t1) t3) +(Join (Join t3 t1) t2) + +0 200 0 0 0 300 +1 201 1 1 1 301 +2 202 2 2 2 302 +*/ + +-- Test whether the optimizer enumerates all 3-join orders. (It don't currently) select * from t2, t1, t3 where t1v1 = t2v1 and t1v2 = t3v2; /* -(Join t1 (Join t2 t3)) -(Join t1 (Join t3 t2)) (Join t2 (Join t1 t3)) (Join t2 (Join t3 t1)) (Join t3 (Join t1 t2)) @@ -37,21 +53,17 @@ select * from t2, t1, t3 where t1v1 = t2v1 and t1v2 = t3v2; (Join (Join t1 t2) t3) (Join (Join t1 t3) t2) (Join (Join t2 t1) t3) -(Join (Join t2 t3) t1) (Join (Join t3 t1) t2) -(Join (Join t3 t2) t1) 0 200 0 0 0 300 1 201 1 1 1 301 2 202 2 2 2 302 */ --- Test whether the optimizer enumerates all 3-join orders. +-- Test whether the optimizer enumerates all 3-join orders. (It don't currently) select * from t1, t2, t3 where t1v1 = t2v1 and t1v2 = t3v2; /* -(Join t1 (Join t2 t3)) -(Join t1 (Join t3 t2)) (Join t2 (Join t1 t3)) (Join t2 (Join t3 t1)) (Join t3 (Join t1 t2)) @@ -59,9 +71,7 @@ select * from t1, t2, t3 where t1v1 = t2v1 and t1v2 = t3v2; (Join (Join t1 t2) t3) (Join (Join t1 t3) t2) (Join (Join t2 t1) t3) -(Join (Join t2 t3) t1) (Join (Join t3 t1) t2) -(Join (Join t3 t2) t1) 0 0 0 200 0 300 1 1 1 201 1 301 diff --git a/optd-sqlplannertest/tests/joins/join_enumerate.yml b/optd-sqlplannertest/tests/joins/join_enumerate.yml index 9417777c..dcd8a757 100644 --- a/optd-sqlplannertest/tests/joins/join_enumerate.yml +++ b/optd-sqlplannertest/tests/joins/join_enumerate.yml @@ -11,17 +11,24 @@ select * from t2, t1 where t1v1 = t2v1; desc: Test whether the optimizer enumerates all 2-join orders. tasks: - - explain:logical_join_orders + # well actually pruning doesn't matter b/c join order is logical, but we are now missing join orders with t1 as the outer table + - explain[disable_pruning]:logical_join_orders + - execute +- sql: | + select * from t2, t1, t3 where t1v1 = t2v1 and t1v1 = t3v2; + desc: Test whether the optimizer enumerates all 3-join orders. (It should) + tasks: + - explain[disable_pruning]:logical_join_orders - execute - sql: | select * from t2, t1, t3 where t1v1 = t2v1 and t1v2 = t3v2; - desc: Test whether the optimizer enumerates all 3-join orders. + desc: Test whether the optimizer enumerates all 3-join orders. (It don't currently) tasks: - - explain:logical_join_orders + - explain[disable_pruning]:logical_join_orders - execute - sql: | select * from t1, t2, t3 where t1v1 = t2v1 and t1v2 = t3v2; - desc: Test whether the optimizer enumerates all 3-join orders. + desc: Test whether the optimizer enumerates all 3-join orders. (It don't currently) tasks: - - explain:logical_join_orders + - explain[disable_pruning]:logical_join_orders - execute diff --git a/optd-sqlplannertest/tests/joins/multi-join.planner.sql b/optd-sqlplannertest/tests/joins/multi-join.planner.sql new file mode 100644 index 00000000..ec29505b --- /dev/null +++ b/optd-sqlplannertest/tests/joins/multi-join.planner.sql @@ -0,0 +1,92 @@ +-- (no id or description) +create table t1(a int, b int); +create table t2(c int, d int); +create table t3(e int, f int); +create table t4(g int, h int); + +/* + +*/ + +-- test 3-way join +select * from t1, t2, t3 where a = c AND d = e; + +/* +LogicalProjection { exprs: [ #0, #1, #2, #3, #4, #5 ] } +└── LogicalFilter + ├── cond:And + │ ├── Eq + │ │ ├── #0 + │ │ └── #2 + │ └── Eq + │ ├── #3 + │ └── #4 + └── LogicalJoin { join_type: Cross, cond: true } + ├── LogicalJoin { join_type: Cross, cond: true } + │ ├── LogicalScan { table: t1 } + │ └── LogicalScan { table: t2 } + └── LogicalScan { table: t3 } +PhysicalHashJoin { join_type: Inner, left_keys: [ #0 ], right_keys: [ #0 ] } +├── PhysicalScan { table: t1 } +└── PhysicalHashJoin { join_type: Inner, left_keys: [ #1 ], right_keys: [ #0 ] } + ├── PhysicalScan { table: t2 } + └── PhysicalScan { table: t3 } +*/ + +-- test 3-way join +select * from t1, t2, t3 where a = c AND b = e; + +/* +LogicalProjection { exprs: [ #0, #1, #2, #3, #4, #5 ] } +└── LogicalFilter + ├── cond:And + │ ├── Eq + │ │ ├── #0 + │ │ └── #2 + │ └── Eq + │ ├── #1 + │ └── #4 + └── LogicalJoin { join_type: Cross, cond: true } + ├── LogicalJoin { join_type: Cross, cond: true } + │ ├── LogicalScan { table: t1 } + │ └── LogicalScan { table: t2 } + └── LogicalScan { table: t3 } +PhysicalHashJoin { join_type: Inner, left_keys: [ #1 ], right_keys: [ #0 ] } +├── PhysicalHashJoin { join_type: Inner, left_keys: [ #0 ], right_keys: [ #0 ] } +│ ├── PhysicalScan { table: t1 } +│ └── PhysicalScan { table: t2 } +└── PhysicalScan { table: t3 } +*/ + +-- test 4-way join +select * from t1, t2, t3, t4 where a = c AND b = e AND f = g; + +/* +LogicalProjection { exprs: [ #0, #1, #2, #3, #4, #5, #6, #7 ] } +└── LogicalFilter + ├── cond:And + │ ├── Eq + │ │ ├── #0 + │ │ └── #2 + │ ├── Eq + │ │ ├── #1 + │ │ └── #4 + │ └── Eq + │ ├── #5 + │ └── #6 + └── LogicalJoin { join_type: Cross, cond: true } + ├── LogicalJoin { join_type: Cross, cond: true } + │ ├── LogicalJoin { join_type: Cross, cond: true } + │ │ ├── LogicalScan { table: t1 } + │ │ └── LogicalScan { table: t2 } + │ └── LogicalScan { table: t3 } + └── LogicalScan { table: t4 } +PhysicalHashJoin { join_type: Inner, left_keys: [ #1 ], right_keys: [ #0 ] } +├── PhysicalHashJoin { join_type: Inner, left_keys: [ #0 ], right_keys: [ #0 ] } +│ ├── PhysicalScan { table: t1 } +│ └── PhysicalScan { table: t2 } +└── PhysicalHashJoin { join_type: Inner, left_keys: [ #1 ], right_keys: [ #0 ] } + ├── PhysicalScan { table: t3 } + └── PhysicalScan { table: t4 } +*/ + diff --git a/optd-sqlplannertest/tests/joins/multi-join.yml b/optd-sqlplannertest/tests/joins/multi-join.yml new file mode 100644 index 00000000..81aeabd7 --- /dev/null +++ b/optd-sqlplannertest/tests/joins/multi-join.yml @@ -0,0 +1,22 @@ +- sql: | + create table t1(a int, b int); + create table t2(c int, d int); + create table t3(e int, f int); + create table t4(g int, h int); + tasks: + - execute +- sql: | + select * from t1, t2, t3 where a = c AND d = e; + desc: test 3-way join + tasks: + - explain:logical_optd,physical_optd +- sql: | + select * from t1, t2, t3 where a = c AND b = e; + desc: test 3-way join + tasks: + - explain:logical_optd,physical_optd +- sql: | + select * from t1, t2, t3, t4 where a = c AND b = e AND f = g; + desc: test 4-way join + tasks: + - explain:logical_optd,physical_optd diff --git a/optd-sqlplannertest/tests/subqueries/subquery_unnesting.planner.sql b/optd-sqlplannertest/tests/subqueries/subquery_unnesting.planner.sql index 74e092c2..a9a92dc9 100644 --- a/optd-sqlplannertest/tests/subqueries/subquery_unnesting.planner.sql +++ b/optd-sqlplannertest/tests/subqueries/subquery_unnesting.planner.sql @@ -135,27 +135,27 @@ LogicalProjection { exprs: [ #0, #1 ] } └── LogicalJoin { join_type: Cross, cond: true } ├── LogicalScan { table: t2 } └── LogicalScan { table: t3 } -PhysicalProjection { exprs: [ #2, #3 ], cost: {compute=9023,io=4000}, stat: {row_cnt=1} } -└── PhysicalHashJoin { join_type: Inner, left_keys: [ #0 ], right_keys: [ #0 ], cost: {compute=9020,io=4000}, stat: {row_cnt=1} } +PhysicalProjection { exprs: [ #2, #3 ], cost: {compute=9021,io=4000}, stat: {row_cnt=1} } +└── PhysicalHashJoin { join_type: Inner, left_keys: [ #0 ], right_keys: [ #0 ], cost: {compute=9018,io=4000}, stat: {row_cnt=1} } ├── PhysicalAgg │ ├── aggrs:Agg(Sum) │ │ └── [ Cast { cast_to: Int64, child: #2 } ] │ ├── groups: [ #1 ] - │ ├── cost: {compute=8018,io=3000} + │ ├── cost: {compute=8016,io=3000} │ ├── stat: {row_cnt=1} - │ └── PhysicalProjection { exprs: [ #2, #0, #1, #3, #4 ], cost: {compute=8010,io=3000}, stat: {row_cnt=1} } - │ └── PhysicalHashJoin { join_type: Inner, left_keys: [ #1 ], right_keys: [ #0 ], cost: {compute=8004,io=3000}, stat: {row_cnt=1} } - │ ├── PhysicalHashJoin { join_type: Inner, left_keys: [ #0 ], right_keys: [ #0 ], cost: {compute=7002,io=2000}, stat: {row_cnt=1} } - │ │ ├── PhysicalFilter - │ │ │ ├── cond:Gt - │ │ │ │ ├── #0 - │ │ │ │ └── 100(i64) - │ │ │ ├── cost: {compute=3000,io=1000} - │ │ │ ├── stat: {row_cnt=1} - │ │ │ └── PhysicalScan { table: t2, cost: {compute=0,io=1000}, stat: {row_cnt=1000} } - │ │ └── PhysicalAgg { aggrs: [], groups: [ #0 ], cost: {compute=3000,io=1000}, stat: {row_cnt=1000} } - │ │ └── PhysicalScan { table: t1, cost: {compute=0,io=1000}, stat: {row_cnt=1000} } - │ └── PhysicalScan { table: t3, cost: {compute=0,io=1000}, stat: {row_cnt=1000} } + │ └── PhysicalHashJoin { join_type: Inner, left_keys: [ #2 ], right_keys: [ #0 ], cost: {compute=8008,io=3000}, stat: {row_cnt=1} } + │ ├── PhysicalProjection { exprs: [ #2, #0, #1 ], cost: {compute=7006,io=2000}, stat: {row_cnt=1} } + │ │ └── PhysicalHashJoin { join_type: Inner, left_keys: [ #0 ], right_keys: [ #0 ], cost: {compute=7002,io=2000}, stat: {row_cnt=1} } + │ │ ├── PhysicalFilter + │ │ │ ├── cond:Gt + │ │ │ │ ├── #0 + │ │ │ │ └── 100(i64) + │ │ │ ├── cost: {compute=3000,io=1000} + │ │ │ ├── stat: {row_cnt=1} + │ │ │ └── PhysicalScan { table: t2, cost: {compute=0,io=1000}, stat: {row_cnt=1000} } + │ │ └── PhysicalAgg { aggrs: [], groups: [ #0 ], cost: {compute=3000,io=1000}, stat: {row_cnt=1000} } + │ │ └── PhysicalScan { table: t1, cost: {compute=0,io=1000}, stat: {row_cnt=1000} } + │ └── PhysicalScan { table: t3, cost: {compute=0,io=1000}, stat: {row_cnt=1000} } └── PhysicalScan { table: t1, cost: {compute=0,io=1000}, stat: {row_cnt=1000} } */ diff --git a/optd-sqlplannertest/tests/tpch/tpch-01-05.planner.sql b/optd-sqlplannertest/tests/tpch/tpch-01-05.planner.sql index 756d2884..c5edf635 100644 --- a/optd-sqlplannertest/tests/tpch/tpch-01-05.planner.sql +++ b/optd-sqlplannertest/tests/tpch/tpch-01-05.planner.sql @@ -334,80 +334,66 @@ PhysicalLimit { skip: 0(u64), fetch: 100(u64) } │ │ └── #1 │ └── SortOrder { order: Asc } │ └── #3 - └── PhysicalProjection { exprs: [ #23, #19, #1, #9, #11, #20, #22, #24 ] } - └── PhysicalFilter - ├── cond:And - │ ├── Eq - │ │ ├── #9 - │ │ └── #4 - │ ├── Eq - │ │ ├── #18 - │ │ └── #5 - │ ├── Eq - │ │ ├── Cast { cast_to: Int64, child: #14 } - │ │ └── 4(i64) - │ ├── Like { expr: #13, pattern: "%TIN", negated: false, case_insensitive: false } - │ ├── Eq - │ │ ├── #21 - │ │ └── #0 - │ ├── Eq - │ │ ├── #2 - │ │ └── #25 - │ ├── Eq - │ │ ├── #26 - │ │ └── "AFRICA" - │ └── Eq - │ ├── #7 - │ └── #29 - └── PhysicalHashJoin { join_type: Inner, left_keys: [ #9 ], right_keys: [ #0 ] } - ├── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ ├── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ │ ├── PhysicalScan { table: nation } - │ │ └── PhysicalScan { table: partsupp } - │ └── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ ├── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ │ ├── PhysicalScan { table: part } - │ │ └── PhysicalScan { table: supplier } - │ └── PhysicalScan { table: region } - └── PhysicalAgg - ├── aggrs:Agg(Min) - │ └── [ #4 ] - ├── groups: [ #1 ] - └── PhysicalFilter - ├── cond:And - │ ├── Eq - │ │ ├── #0 - │ │ └── #1 - │ ├── Eq - │ │ ├── #6 - │ │ └── #2 - │ ├── Eq - │ │ ├── #9 - │ │ └── #13 - │ ├── Eq - │ │ ├── #15 - │ │ └── #17 - │ └── Eq - │ ├── #18 - │ └── "AFRICA" - └── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - ├── PhysicalAgg { aggrs: [], groups: [ #0 ] } - │ └── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ ├── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ │ ├── PhysicalScan { table: part } - │ │ └── PhysicalScan { table: supplier } - │ └── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ ├── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ │ ├── PhysicalScan { table: partsupp } - │ │ └── PhysicalScan { table: nation } - │ └── PhysicalScan { table: region } - └── PhysicalNestedLoopJoin { join_type: Cross, cond: true } - ├── PhysicalNestedLoopJoin { join_type: Cross, cond: true } - │ ├── PhysicalNestedLoopJoin { join_type: Cross, cond: true } - │ │ ├── PhysicalScan { table: partsupp } - │ │ └── PhysicalScan { table: supplier } - │ └── PhysicalScan { table: nation } - └── PhysicalScan { table: region } + └── PhysicalProjection { exprs: [ #21, #17, #4, #7, #9, #18, #20, #22 ] } + └── PhysicalHashJoin { join_type: Inner, left_keys: [ #26, #7 ], right_keys: [ #1, #0 ] } + ├── PhysicalHashJoin { join_type: Inner, left_keys: [ #7, #16 ], right_keys: [ #0, #1 ] } + │ ├── PhysicalHashJoin { join_type: Inner, left_keys: [ #3 ], right_keys: [ #12 ] } + │ │ ├── PhysicalHashJoin { join_type: Inner, left_keys: [ #0 ], right_keys: [ #2 ] } + │ │ │ ├── PhysicalFilter + │ │ │ │ ├── cond:Eq + │ │ │ │ │ ├── #1 + │ │ │ │ │ └── "AFRICA" + │ │ │ │ └── PhysicalScan { table: region } + │ │ │ └── PhysicalScan { table: nation } + │ │ └── PhysicalNestedLoopJoin { join_type: Cross, cond: true } + │ │ ├── PhysicalFilter + │ │ │ ├── cond:And + │ │ │ │ ├── Eq + │ │ │ │ │ ├── Cast { cast_to: Int64, child: #5 } + │ │ │ │ │ └── 4(i64) + │ │ │ │ └── Like { expr: #4, pattern: "%TIN", negated: false, case_insensitive: false } + │ │ │ └── PhysicalScan { table: part } + │ │ └── PhysicalScan { table: supplier } + │ └── PhysicalScan { table: partsupp } + └── PhysicalAgg + ├── aggrs:Agg(Min) + │ └── [ #4 ] + ├── groups: [ #1 ] + └── PhysicalFilter + ├── cond:And + │ ├── Eq + │ │ ├── #0 + │ │ └── #1 + │ ├── Eq + │ │ ├── #6 + │ │ └── #2 + │ ├── Eq + │ │ ├── #9 + │ │ └── #13 + │ ├── Eq + │ │ ├── #15 + │ │ └── #17 + │ └── Eq + │ ├── #18 + │ └── "AFRICA" + └── PhysicalNestedLoopJoin { join_type: Inner, cond: true } + ├── PhysicalAgg { aggrs: [], groups: [ #0 ] } + │ └── PhysicalNestedLoopJoin { join_type: Cross, cond: true } + │ ├── PhysicalNestedLoopJoin { join_type: Cross, cond: true } + │ │ ├── PhysicalNestedLoopJoin { join_type: Cross, cond: true } + │ │ │ ├── PhysicalNestedLoopJoin { join_type: Cross, cond: true } + │ │ │ │ ├── PhysicalScan { table: part } + │ │ │ │ └── PhysicalScan { table: supplier } + │ │ │ └── PhysicalScan { table: partsupp } + │ │ └── PhysicalScan { table: nation } + │ └── PhysicalScan { table: region } + └── PhysicalNestedLoopJoin { join_type: Cross, cond: true } + ├── PhysicalNestedLoopJoin { join_type: Cross, cond: true } + │ ├── PhysicalNestedLoopJoin { join_type: Cross, cond: true } + │ │ ├── PhysicalScan { table: partsupp } + │ │ └── PhysicalScan { table: supplier } + │ └── PhysicalScan { table: nation } + └── PhysicalScan { table: region } */ -- TPC-H Q3 @@ -599,38 +585,24 @@ PhysicalSort │ └── #23 ├── groups: [ #41 ] └── PhysicalHashJoin { join_type: Inner, left_keys: [ #42 ], right_keys: [ #0 ] } - ├── PhysicalFilter - │ ├── cond:And - │ │ ├── Eq - │ │ │ ├── #0 - │ │ │ └── #9 - │ │ ├── Eq - │ │ │ ├── #17 - │ │ │ └── #8 - │ │ ├── Eq - │ │ │ ├── #19 - │ │ │ └── #33 - │ │ ├── Eq - │ │ │ ├── #3 - │ │ │ └── #36 - │ │ ├── Eq - │ │ │ ├── #36 - │ │ │ └── #40 - │ │ ├── Geq - │ │ │ ├── #12 - │ │ │ └── Cast { cast_to: Date32, child: "2023-01-01" } - │ │ └── Lt - │ │ ├── #12 - │ │ └── Cast { cast_to: Date32, child: "2024-01-01" } - │ └── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ ├── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ │ ├── PhysicalScan { table: customer } - │ │ └── PhysicalScan { table: orders } - │ └── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ ├── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ │ ├── PhysicalScan { table: lineitem } - │ │ └── PhysicalScan { table: supplier } - │ └── PhysicalScan { table: nation } + ├── PhysicalHashJoin { join_type: Inner, left_keys: [ #36 ], right_keys: [ #0 ] } + │ ├── PhysicalHashJoin { join_type: Inner, left_keys: [ #19, #3 ], right_keys: [ #0, #3 ] } + │ │ ├── PhysicalProjection { exprs: [ #25, #26, #27, #28, #29, #30, #31, #32, #0, #1, #2, #3, #4, #5, #6, #7, #8, #9, #10, #11, #12, #13, #14, #15, #16, #17, #18, #19, #20, #21, #22, #23, #24 ] } + │ │ │ └── PhysicalHashJoin { join_type: Inner, left_keys: [ #1 ], right_keys: [ #0 ] } + │ │ │ ├── PhysicalHashJoin { join_type: Inner, left_keys: [ #0 ], right_keys: [ #0 ] } + │ │ │ │ ├── PhysicalFilter + │ │ │ │ │ ├── cond:And + │ │ │ │ │ │ ├── Geq + │ │ │ │ │ │ │ ├── #4 + │ │ │ │ │ │ │ └── Cast { cast_to: Date32, child: "2023-01-01" } + │ │ │ │ │ │ └── Lt + │ │ │ │ │ │ ├── #4 + │ │ │ │ │ │ └── Cast { cast_to: Date32, child: "2024-01-01" } + │ │ │ │ │ └── PhysicalScan { table: orders } + │ │ │ │ └── PhysicalScan { table: lineitem } + │ │ │ └── PhysicalScan { table: customer } + │ │ └── PhysicalScan { table: supplier } + │ └── PhysicalScan { table: nation } └── PhysicalFilter ├── cond:Eq │ ├── #1 diff --git a/optd-sqlplannertest/tests/tpch/tpch-06-10.planner.sql b/optd-sqlplannertest/tests/tpch/tpch-06-10.planner.sql index accf3d97..288fff94 100644 --- a/optd-sqlplannertest/tests/tpch/tpch-06-10.planner.sql +++ b/optd-sqlplannertest/tests/tpch/tpch-06-10.planner.sql @@ -295,30 +295,17 @@ PhysicalSort │ └── Eq │ ├── #45 │ └── "FRANCE" - ├── PhysicalFilter - │ ├── cond:And - │ │ ├── Eq - │ │ │ ├── #0 - │ │ │ └── #9 - │ │ ├── Eq - │ │ │ ├── #23 - │ │ │ └── #7 - │ │ ├── Eq - │ │ │ ├── #32 - │ │ │ └── #24 - │ │ ├── Eq - │ │ │ ├── #3 - │ │ │ └── #40 - │ │ └── Between { child: #17, lower: Cast { cast_to: Date32, child: "1995-01-01" }, upper: Cast { cast_to: Date32, child: "1996-12-31" } } - │ └── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ ├── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ │ ├── PhysicalScan { table: supplier } - │ │ └── PhysicalScan { table: lineitem } - │ └── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ ├── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ │ ├── PhysicalScan { table: orders } - │ │ └── PhysicalScan { table: customer } - │ └── PhysicalScan { table: nation } + ├── PhysicalHashJoin { join_type: Inner, left_keys: [ #3 ], right_keys: [ #0 ] } + │ ├── PhysicalHashJoin { join_type: Inner, left_keys: [ #24 ], right_keys: [ #0 ] } + │ │ ├── PhysicalProjection { exprs: [ #16, #17, #18, #19, #20, #21, #22, #0, #1, #2, #3, #4, #5, #6, #7, #8, #9, #10, #11, #12, #13, #14, #15, #23, #24, #25, #26, #27, #28, #29, #30, #31 ] } + │ │ │ └── PhysicalHashJoin { join_type: Inner, left_keys: [ #0 ], right_keys: [ #0 ] } + │ │ │ ├── PhysicalHashJoin { join_type: Inner, left_keys: [ #2 ], right_keys: [ #0 ] } + │ │ │ │ ├── PhysicalFilter { cond: Between { child: #10, lower: Cast { cast_to: Date32, child: "1995-01-01" }, upper: Cast { cast_to: Date32, child: "1996-12-31" } } } + │ │ │ │ │ └── PhysicalScan { table: lineitem } + │ │ │ │ └── PhysicalScan { table: supplier } + │ │ │ └── PhysicalScan { table: orders } + │ │ └── PhysicalScan { table: customer } + │ └── PhysicalScan { table: nation } └── PhysicalScan { table: nation } */ @@ -472,43 +459,24 @@ PhysicalSort │ │ └── #22 │ └── #54 └── PhysicalHashJoin { join_type: Inner, left_keys: [ #51 ], right_keys: [ #0 ] } - ├── PhysicalFilter - │ ├── cond:And - │ │ ├── Eq - │ │ │ ├── #0 - │ │ │ └── #17 - │ │ ├── Eq - │ │ │ ├── #9 - │ │ │ └── #18 - │ │ ├── Eq - │ │ │ ├── #16 - │ │ │ └── #32 - │ │ ├── Eq - │ │ │ ├── #33 - │ │ │ └── #41 - │ │ ├── Eq - │ │ │ ├── #44 - │ │ │ └── #49 - │ │ ├── Eq - │ │ │ ├── #12 - │ │ │ └── #53 - │ │ ├── Between { child: #36, lower: Cast { cast_to: Date32, child: "1995-01-01" }, upper: Cast { cast_to: Date32, child: "1996-12-31" } } - │ │ └── Eq - │ │ ├── #4 - │ │ └── "ECONOMY ANODIZED STEEL" - │ └── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ ├── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ │ ├── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ │ │ ├── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ │ │ │ ├── PhysicalScan { table: part } - │ │ │ │ └── PhysicalScan { table: supplier } - │ │ │ └── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ │ │ ├── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ │ │ │ ├── PhysicalScan { table: lineitem } - │ │ │ │ └── PhysicalScan { table: orders } - │ │ │ └── PhysicalScan { table: customer } - │ │ └── PhysicalScan { table: nation } - │ └── PhysicalScan { table: nation } + ├── PhysicalHashJoin { join_type: Inner, left_keys: [ #12 ], right_keys: [ #0 ] } + │ ├── PhysicalHashJoin { join_type: Inner, left_keys: [ #16 ], right_keys: [ #0 ] } + │ │ ├── PhysicalHashJoin { join_type: Inner, left_keys: [ #0, #9 ], right_keys: [ #1, #2 ] } + │ │ │ ├── PhysicalNestedLoopJoin { join_type: Cross, cond: true } + │ │ │ │ ├── PhysicalFilter + │ │ │ │ │ ├── cond:Eq + │ │ │ │ │ │ ├── #4 + │ │ │ │ │ │ └── "ECONOMY ANODIZED STEEL" + │ │ │ │ │ └── PhysicalScan { table: part } + │ │ │ │ └── PhysicalScan { table: supplier } + │ │ │ └── PhysicalScan { table: lineitem } + │ │ └── PhysicalHashJoin { join_type: Inner, left_keys: [ #12 ], right_keys: [ #0 ] } + │ │ ├── PhysicalHashJoin { join_type: Inner, left_keys: [ #1 ], right_keys: [ #0 ] } + │ │ │ ├── PhysicalFilter { cond: Between { child: #4, lower: Cast { cast_to: Date32, child: "1995-01-01" }, upper: Cast { cast_to: Date32, child: "1996-12-31" } } } + │ │ │ │ └── PhysicalScan { table: orders } + │ │ │ └── PhysicalScan { table: customer } + │ │ └── PhysicalScan { table: nation } + │ └── PhysicalScan { table: nation } └── PhysicalFilter ├── cond:Eq │ ├── #1 @@ -633,33 +601,16 @@ PhysicalSort │ ├── #35 │ └── #20 └── PhysicalHashJoin { join_type: Inner, left_keys: [ #12 ], right_keys: [ #0 ] } - ├── PhysicalFilter - │ ├── cond:And - │ │ ├── Eq - │ │ │ ├── #9 - │ │ │ └── #18 - │ │ ├── Eq - │ │ │ ├── #33 - │ │ │ └── #18 - │ │ ├── Eq - │ │ │ ├── #32 - │ │ │ └── #17 - │ │ ├── Eq - │ │ │ ├── #0 - │ │ │ └── #17 - │ │ ├── Eq - │ │ │ ├── #37 - │ │ │ └── #16 - │ │ └── Like { expr: #1, pattern: "%green%", negated: false, case_insensitive: false } - │ └── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ ├── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ │ ├── PhysicalScan { table: part } - │ │ └── PhysicalScan { table: supplier } - │ └── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ ├── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ │ ├── PhysicalScan { table: lineitem } - │ │ └── PhysicalScan { table: partsupp } - │ └── PhysicalScan { table: orders } + ├── PhysicalHashJoin { join_type: Inner, left_keys: [ #16 ], right_keys: [ #0 ] } + │ ├── PhysicalHashJoin { join_type: Inner, left_keys: [ #18, #17 ], right_keys: [ #1, #0 ] } + │ │ ├── PhysicalHashJoin { join_type: Inner, left_keys: [ #9, #0 ], right_keys: [ #2, #1 ] } + │ │ │ ├── PhysicalNestedLoopJoin { join_type: Cross, cond: true } + │ │ │ │ ├── PhysicalFilter { cond: Like { expr: #1, pattern: "%green%", negated: false, case_insensitive: false } } + │ │ │ │ │ └── PhysicalScan { table: part } + │ │ │ │ └── PhysicalScan { table: supplier } + │ │ │ └── PhysicalScan { table: lineitem } + │ │ └── PhysicalScan { table: partsupp } + │ └── PhysicalScan { table: orders } └── PhysicalScan { table: nation } */ @@ -780,33 +731,16 @@ PhysicalSort │ ├── #35 │ └── #20 └── PhysicalHashJoin { join_type: Inner, left_keys: [ #12 ], right_keys: [ #0 ] } - ├── PhysicalFilter - │ ├── cond:And - │ │ ├── Eq - │ │ │ ├── #9 - │ │ │ └── #18 - │ │ ├── Eq - │ │ │ ├── #33 - │ │ │ └── #18 - │ │ ├── Eq - │ │ │ ├── #32 - │ │ │ └── #17 - │ │ ├── Eq - │ │ │ ├── #0 - │ │ │ └── #17 - │ │ ├── Eq - │ │ │ ├── #37 - │ │ │ └── #16 - │ │ └── Like { expr: #1, pattern: "%green%", negated: false, case_insensitive: false } - │ └── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ ├── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ │ ├── PhysicalScan { table: part } - │ │ └── PhysicalScan { table: supplier } - │ └── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ ├── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ │ ├── PhysicalScan { table: lineitem } - │ │ └── PhysicalScan { table: partsupp } - │ └── PhysicalScan { table: orders } + ├── PhysicalHashJoin { join_type: Inner, left_keys: [ #16 ], right_keys: [ #0 ] } + │ ├── PhysicalHashJoin { join_type: Inner, left_keys: [ #18, #17 ], right_keys: [ #1, #0 ] } + │ │ ├── PhysicalHashJoin { join_type: Inner, left_keys: [ #9, #0 ], right_keys: [ #2, #1 ] } + │ │ │ ├── PhysicalNestedLoopJoin { join_type: Cross, cond: true } + │ │ │ │ ├── PhysicalFilter { cond: Like { expr: #1, pattern: "%green%", negated: false, case_insensitive: false } } + │ │ │ │ │ └── PhysicalScan { table: part } + │ │ │ │ └── PhysicalScan { table: supplier } + │ │ │ └── PhysicalScan { table: lineitem } + │ │ └── PhysicalScan { table: partsupp } + │ └── PhysicalScan { table: orders } └── PhysicalScan { table: nation } */ @@ -900,29 +834,27 @@ PhysicalLimit { skip: 0(u64), fetch: 20(u64) } │ ├── Cast { cast_to: Decimal128(20, 0), child: 1(i64) } │ └── #23 ├── groups: [ #0, #1, #5, #4, #34, #2, #7 ] - └── PhysicalHashJoin { join_type: Inner, left_keys: [ #8, #3 ], right_keys: [ #0, #16 ] } - ├── PhysicalFilter - │ ├── cond:And - │ │ ├── Eq - │ │ │ ├── #0 - │ │ │ └── #9 - │ │ ├── Geq - │ │ │ ├── #12 - │ │ │ └── Cast { cast_to: Date32, child: "1993-07-01" } - │ │ └── Lt - │ │ ├── #12 - │ │ └── Add - │ │ ├── Cast { cast_to: Date32, child: "1993-07-01" } - │ │ └── INTERVAL_MONTH_DAY_NANO (3, 0, 0) - │ └── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - │ ├── PhysicalScan { table: customer } - │ └── PhysicalScan { table: orders } - └── PhysicalFilter - ├── cond:Eq - │ ├── #8 - │ └── "R" - └── PhysicalNestedLoopJoin { join_type: Inner, cond: true } - ├── PhysicalScan { table: lineitem } - └── PhysicalScan { table: nation } + └── PhysicalHashJoin { join_type: Inner, left_keys: [ #3 ], right_keys: [ #0 ] } + ├── PhysicalProjection { exprs: [ #25, #26, #27, #28, #29, #30, #31, #32, #16, #17, #18, #19, #20, #21, #22, #23, #24, #0, #1, #2, #3, #4, #5, #6, #7, #8, #9, #10, #11, #12, #13, #14, #15 ] } + │ └── PhysicalHashJoin { join_type: Inner, left_keys: [ #0 ], right_keys: [ #0 ] } + │ ├── PhysicalFilter + │ │ ├── cond:Eq + │ │ │ ├── #8 + │ │ │ └── "R" + │ │ └── PhysicalScan { table: lineitem } + │ └── PhysicalHashJoin { join_type: Inner, left_keys: [ #1 ], right_keys: [ #0 ] } + │ ├── PhysicalFilter + │ │ ├── cond:And + │ │ │ ├── Geq + │ │ │ │ ├── #4 + │ │ │ │ └── Cast { cast_to: Date32, child: "1993-07-01" } + │ │ │ └── Lt + │ │ │ ├── #4 + │ │ │ └── Add + │ │ │ ├── Cast { cast_to: Date32, child: "1993-07-01" } + │ │ │ └── INTERVAL_MONTH_DAY_NANO (3, 0, 0) + │ │ └── PhysicalScan { table: orders } + │ └── PhysicalScan { table: customer } + └── PhysicalScan { table: nation } */ diff --git a/optd-sqlplannertest/tests/tpch/tpch-11-15.planner.sql b/optd-sqlplannertest/tests/tpch/tpch-11-15.planner.sql index 2ff1c4cb..235fe273 100644 --- a/optd-sqlplannertest/tests/tpch/tpch-11-15.planner.sql +++ b/optd-sqlplannertest/tests/tpch/tpch-11-15.planner.sql @@ -207,15 +207,15 @@ PhysicalSort │ │ └── Cast { cast_to: Decimal128(10, 0), child: #1 } │ ├── groups: [ #0 ] │ └── PhysicalProjection { exprs: [ #11, #13, #14 ] } - │ └── PhysicalHashJoin { join_type: Inner, left_keys: [ #0 ], right_keys: [ #3 ] } - │ ├── PhysicalFilter - │ │ ├── cond:Eq - │ │ │ ├── #1 - │ │ │ └── "CHINA" - │ │ └── PhysicalScan { table: nation } - │ └── PhysicalHashJoin { join_type: Inner, left_keys: [ #0 ], right_keys: [ #1 ] } - │ ├── PhysicalScan { table: supplier } - │ └── PhysicalScan { table: partsupp } + │ └── PhysicalHashJoin { join_type: Inner, left_keys: [ #4 ], right_keys: [ #1 ] } + │ ├── PhysicalHashJoin { join_type: Inner, left_keys: [ #0 ], right_keys: [ #3 ] } + │ │ ├── PhysicalFilter + │ │ │ ├── cond:Eq + │ │ │ │ ├── #1 + │ │ │ │ └── "CHINA" + │ │ │ └── PhysicalScan { table: nation } + │ │ └── PhysicalScan { table: supplier } + │ └── PhysicalScan { table: partsupp } └── PhysicalProjection ├── exprs:Cast │ ├── cast_to: Decimal128(38, 15) diff --git a/optd-sqlplannertest/tests/tpch/tpch-16-20.planner.sql b/optd-sqlplannertest/tests/tpch/tpch-16-20.planner.sql index 88e5b22d..c8a8deb8 100644 --- a/optd-sqlplannertest/tests/tpch/tpch-16-20.planner.sql +++ b/optd-sqlplannertest/tests/tpch/tpch-16-20.planner.sql @@ -384,7 +384,7 @@ PhysicalAgg │ └── Eq │ ├── #13 │ └── "DELIVER IN PERSON" - └── PhysicalNestedLoopJoin { join_type: Inner, cond: true } + └── PhysicalNestedLoopJoin { join_type: Cross, cond: true } ├── PhysicalScan { table: lineitem } └── PhysicalScan { table: part } */