diff --git a/optd-datafusion-repr/src/cost/base_cost/join.rs b/optd-datafusion-repr/src/cost/base_cost/join.rs index b99bf7f7..a2eaa374 100644 --- a/optd-datafusion-repr/src/cost/base_cost/join.rs +++ b/optd-datafusion-repr/src/cost/base_cost/join.rs @@ -451,6 +451,10 @@ impl< predicate: &EqPredicate, past_eq_columns: &mut EqBaseTableColumnSets, ) -> f64 { + if predicate.left == predicate.right { + // self-join, TODO: is this correct? + return 1.0; + } // To find the adjustment, we need to know the selectivity of the graph before `predicate` is added. // // There are two cases: (1) adding `predicate` does not change the # of connected components, and diff --git a/optd-datafusion-repr/src/properties/column_ref.rs b/optd-datafusion-repr/src/properties/column_ref.rs index b86b8658..1c75a322 100644 --- a/optd-datafusion-repr/src/properties/column_ref.rs +++ b/optd-datafusion-repr/src/properties/column_ref.rs @@ -362,7 +362,9 @@ impl PropertyBuilder for ColumnRefPropertyBuilder { GroupColumnRefs::new(column_refs, child.output_correlation.clone()) } // Should account for all physical join types. - OptRelNodeTyp::Join(join_type) | OptRelNodeTyp::RawDepJoin(join_type) | OptRelNodeTyp::DepJoin(join_type)=> { + OptRelNodeTyp::Join(join_type) + | OptRelNodeTyp::RawDepJoin(join_type) + | OptRelNodeTyp::DepJoin(join_type) => { // Concatenate left and right children column refs. let column_refs = Self::concat_children_col_refs(&children[0..2]); // Merge the equal columns of two children as input correlation. @@ -465,12 +467,14 @@ impl PropertyBuilder for ColumnRefPropertyBuilder { GroupColumnRefs::new(column_refs, correlation) } OptRelNodeTyp::Constant(_) - | OptRelNodeTyp::ExternColumnRef // TODO Possibly very very wrong---consult cost model team | OptRelNodeTyp::Func(_) | OptRelNodeTyp::DataType(_) | OptRelNodeTyp::Between | OptRelNodeTyp::Like - | OptRelNodeTyp::InList => GroupColumnRefs::new(vec![ColumnRef::Derived], None), + | OptRelNodeTyp::InList + | OptRelNodeTyp::ExternColumnRef => { + GroupColumnRefs::new(vec![ColumnRef::Derived], None) + } _ => unimplemented!("Unsupported rel node type {:?}", typ), } } diff --git a/optd-sqlplannertest/tests/joins/self-join.planner.sql b/optd-sqlplannertest/tests/joins/self-join.planner.sql new file mode 100644 index 00000000..c8e615cb --- /dev/null +++ b/optd-sqlplannertest/tests/joins/self-join.planner.sql @@ -0,0 +1,31 @@ +-- (no id or description) +create table t1(t1v1 int, t1v2 int); +create table t2(t2v1 int, t2v3 int); +insert into t1 values (0, 0), (1, 1), (2, 2); +insert into t2 values (0, 200), (1, 201), (2, 202); + +/* +3 +3 +*/ + +-- test self join +select * from t1 as a, t1 as b where a.t1v1 = b.t1v1; + +/* +LogicalProjection { exprs: [ #0, #1, #2, #3 ] } +└── LogicalFilter + ├── cond:Eq + │ ├── #0 + │ └── #2 + └── LogicalJoin { join_type: Cross, cond: true } + ├── LogicalScan { table: t1 } + └── LogicalScan { table: t1 } +PhysicalHashJoin { join_type: Inner, left_keys: [ #0 ], right_keys: [ #0 ] } +├── PhysicalScan { table: t1 } +└── PhysicalScan { table: t1 } +0 0 0 0 +1 1 1 1 +2 2 2 2 +*/ + diff --git a/optd-sqlplannertest/tests/joins/self-join.yml b/optd-sqlplannertest/tests/joins/self-join.yml new file mode 100644 index 00000000..627986eb --- /dev/null +++ b/optd-sqlplannertest/tests/joins/self-join.yml @@ -0,0 +1,13 @@ +- sql: | + create table t1(t1v1 int, t1v2 int); + create table t2(t2v1 int, t2v3 int); + insert into t1 values (0, 0), (1, 1), (2, 2); + insert into t2 values (0, 200), (1, 201), (2, 202); + tasks: + - execute +- sql: | + select * from t1 as a, t1 as b where a.t1v1 = b.t1v1; + desc: test self join + tasks: + - explain:logical_optd,physical_optd + - execute