diff --git a/executor/jointest/join_test.go b/executor/jointest/join_test.go index 99eb3d46b62ed..98050413fc436 100644 --- a/executor/jointest/join_test.go +++ b/executor/jointest/join_test.go @@ -2788,6 +2788,30 @@ func TestIssue37932(t *testing.T) { require.NoError(t, err) } +func TestIssue30244(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustExec("drop table if exists t1,t2,t3,t4;") + tk.MustExec("create table t1 (c int, b int);") + tk.MustExec("create table t2 (a int, b int);") + tk.MustExec("create table t3 (b int, c int);") + tk.MustExec("create table t4 (y int, c int);") + err := tk.ExecToErr("select * from t1 natural join (t3 cross join t4);") + require.NotNil(t, err) + require.Equal(t, err.Error(), "[planner:1052]Column 'c' in from clause is ambiguous") + err = tk.ExecToErr("select * from (t3 cross join t4) natural join t1;") + require.NotNil(t, err) + require.Equal(t, err.Error(), "[planner:1052]Column 'c' in from clause is ambiguous") + err = tk.ExecToErr("select * from (t1 join t2 on t1.b=t2.b) natural join (t3 natural join t4);") + require.NotNil(t, err) + require.Equal(t, err.Error(), "[planner:1052]Column 'b' in from clause is ambiguous") + err = tk.ExecToErr("select * from (t3 natural join t4) natural join (t1 join t2 on t1.b=t2.b);") + require.NotNil(t, err) + require.Equal(t, err.Error(), "[planner:1052]Column 'b' in from clause is ambiguous") +} + func TestOuterJoin(t *testing.T) { store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index 3fdbac30eed65..526327586b363 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -1114,6 +1114,52 @@ func (b *PlanBuilder) coalesceCommonColumns(p *LogicalJoin, leftPlan, rightPlan if err != nil { return err } + } else { + // Even with no using filter, we still should check the checkAmbiguous name before we try to find the common column from both side. + // (t3 cross join t4) natural join t1 + // t1 natural join (t3 cross join t4) + // t3 and t4 may generate the same name column from cross join. + // for every common column of natural join, the name from right or left should be exactly one. + commonNames := make([]string, 0, len(lNames)) + lNameMap := make(map[string]int, len(lNames)) + rNameMap := make(map[string]int, len(rNames)) + for _, name := range lNames { + // Natural join should ignore _tidb_rowid + if name.ColName.L == "_tidb_rowid" { + continue + } + // record left map + if cnt, ok := lNameMap[name.ColName.L]; ok { + lNameMap[name.ColName.L] = cnt + 1 + } else { + lNameMap[name.ColName.L] = 1 + } + } + for _, name := range rNames { + // Natural join should ignore _tidb_rowid + if name.ColName.L == "_tidb_rowid" { + continue + } + // record right map + if cnt, ok := rNameMap[name.ColName.L]; ok { + rNameMap[name.ColName.L] = cnt + 1 + } else { + rNameMap[name.ColName.L] = 1 + } + // check left map + if cnt, ok := lNameMap[name.ColName.L]; ok { + if cnt > 1 { + return ErrAmbiguous.GenWithStackByArgs(name.ColName.L, "from clause") + } + commonNames = append(commonNames, name.ColName.L) + } + } + // check right map + for _, commonName := range commonNames { + if rNameMap[commonName] > 1 { + return ErrAmbiguous.GenWithStackByArgs(commonName, "from clause") + } + } } // Find out all the common columns and put them ahead.