diff --git a/planner/core/exhaust_physical_plans.go b/planner/core/exhaust_physical_plans.go index d9686dac8ebcd..dedf980ee063f 100644 --- a/planner/core/exhaust_physical_plans.go +++ b/planner/core/exhaust_physical_plans.go @@ -467,6 +467,11 @@ func (p *LogicalJoin) constructIndexJoin( lhs, ok1 := c.GetArgs()[0].(*expression.Column) rhs, ok2 := c.GetArgs()[1].(*expression.Column) if ok1 && ok2 { + if lhs.InOperand || rhs.InOperand { + // if this other-cond is from a `[not] in` sub-query, do not convert it into eq-cond since + // IndexJoin cannot deal with NULL correctly in this case; please see #25799 for more details. + continue + } outerSchema, innerSchema := p.Children()[outerIdx].Schema(), p.Children()[1-outerIdx].Schema() if outerSchema.Contains(lhs) && innerSchema.Contains(rhs) { outerHashKeys = append(outerHashKeys, lhs) diff --git a/planner/core/integration_test.go b/planner/core/integration_test.go index d33b1f5d31e11..55dbe6a33960b 100644 --- a/planner/core/integration_test.go +++ b/planner/core/integration_test.go @@ -3474,6 +3474,18 @@ func (s *testIntegrationSuite) TestIssue24281(c *C) { "UNION select 1 as v1, 2 as v2") } +func (s *testIntegrationSuite) TestIssue25799(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2") + tk.MustExec(`create table t1 (a float default null, b smallint(6) DEFAULT NULL)`) + tk.MustExec(`insert into t1 values (1, 1)`) + tk.MustExec(`create table t2 (a float default null, b tinyint(4) DEFAULT NULL, key b (b))`) + tk.MustExec(`insert into t2 values (null, 1)`) + tk.HasPlan(`select /*+ TIDB_INLJ(t2@sel_2) */ t1.a, t1.b from t1 where t1.a not in (select t2.a from t2 where t1.b=t2.b)`, `IndexJoin`) + tk.MustQuery(`select /*+ TIDB_INLJ(t2@sel_2) */ t1.a, t1.b from t1 where t1.a not in (select t2.a from t2 where t1.b=t2.b)`).Check(testkit.Rows()) +} + func (s *testIntegrationSuite) TestLimitWindowColPrune(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec("use test")