diff --git a/executor/merge_join_test.go b/executor/merge_join_test.go index 0904c83685fae..d2a5ee9dc22bc 100644 --- a/executor/merge_join_test.go +++ b/executor/merge_join_test.go @@ -289,6 +289,18 @@ func (s *testSuite) TestMergeJoin(c *C) { result.Check(testkit.Rows("1", "2", "3", "4", "5", "6", "7")) result = tk.MustQuery("select /*+ TIDB_SMJ(a, b) */ a.c1 from t a , (select * from t1 limit 3) b where a.c1 = b.c1 order by b.c1;") result.Check(testkit.Rows("1", "2", "3")) + // Test LogicalSelection under LogicalJoin. + result = tk.MustQuery("select /*+ TIDB_SMJ(a, b) */ a.c1 from t a , (select * from t1 limit 3) b where a.c1 = b.c1 and b.c1 is not null order by b.c1;") + result.Check(testkit.Rows("1", "2", "3")) + tk.MustExec("begin;") + // Test LogicalLock under LogicalJoin. + result = tk.MustQuery("select /*+ TIDB_SMJ(a, b) */ a.c1 from t a , (select * from t1 for update) b where a.c1 = b.c1 order by a.c1;") + result.Check(testkit.Rows("1", "2", "3", "4", "5", "6", "7")) + // Test LogicalUnionScan under LogicalJoin. + tk.MustExec("insert into t1 values(8);") + result = tk.MustQuery("select /*+ TIDB_SMJ(a, b) */ a.c1 from t a , t1 b where a.c1 = b.c1;") + result.Check(testkit.Rows("1", "2", "3", "4", "5", "6", "7")) + tk.MustExec("rollback;") plannercore.AllowCartesianProduct = false _, err := tk.Exec("select /*+ TIDB_SMJ(t,t1) */ * from t, t1") diff --git a/planner/core/exhaust_physical_plans.go b/planner/core/exhaust_physical_plans.go index bddea850be232..dd076d3f9ce52 100644 --- a/planner/core/exhaust_physical_plans.go +++ b/planner/core/exhaust_physical_plans.go @@ -29,7 +29,8 @@ import ( ) func (p *LogicalUnionScan) exhaustPhysicalPlans(prop *property.PhysicalProperty) []PhysicalPlan { - us := PhysicalUnionScan{Conditions: p.conditions}.init(p.ctx, p.stats, prop) + childProp := prop.Clone() + us := PhysicalUnionScan{Conditions: p.conditions}.init(p.ctx, p.stats, childProp) return []PhysicalPlan{us} } @@ -846,9 +847,10 @@ func (la *LogicalAggregation) exhaustPhysicalPlans(prop *property.PhysicalProper } func (p *LogicalSelection) exhaustPhysicalPlans(prop *property.PhysicalProperty) []PhysicalPlan { + childProp := prop.Clone() sel := PhysicalSelection{ Conditions: p.Conditions, - }.init(p.ctx, p.stats.ScaleByExpectCnt(prop.ExpectedCnt), prop) + }.init(p.ctx, p.stats.ScaleByExpectCnt(prop.ExpectedCnt), childProp) return []PhysicalPlan{sel} } @@ -869,9 +871,10 @@ func (p *LogicalLimit) exhaustPhysicalPlans(prop *property.PhysicalProperty) []P } func (p *LogicalLock) exhaustPhysicalPlans(prop *property.PhysicalProperty) []PhysicalPlan { + childProp := prop.Clone() lock := PhysicalLock{ Lock: p.Lock, - }.init(p.ctx, p.stats.ScaleByExpectCnt(prop.ExpectedCnt), prop) + }.init(p.ctx, p.stats.ScaleByExpectCnt(prop.ExpectedCnt), childProp) return []PhysicalPlan{lock} } diff --git a/planner/property/physical_property.go b/planner/property/physical_property.go index e8c818b3b2fd9..b59c7d16267fd 100644 --- a/planner/property/physical_property.go +++ b/planner/property/physical_property.go @@ -101,3 +101,17 @@ func (p *PhysicalProperty) HashCode() []byte { func (p *PhysicalProperty) String() string { return fmt.Sprintf("Prop{cols: %v, desc: %v, TaskTp: %s, expectedCount: %v}", p.Cols, p.Desc, p.TaskTp, p.ExpectedCnt) } + +// Clone returns a copy of PhysicalProperty. Currently, this function is only used to build new +// required property for children plan in `exhaustPhysicalPlans`, so we don't copy `Enforced` field +// because if `Enforced` is true, the `Cols` must be empty now, this makes `Enforced` meaningless +// for children nodes. +func (p *PhysicalProperty) Clone() *PhysicalProperty { + prop := &PhysicalProperty{ + Cols: p.Cols, + Desc: p.Desc, + TaskTp: p.TaskTp, + ExpectedCnt: p.ExpectedCnt, + } + return prop +}