diff --git a/.gitignore b/.gitignore index e01efc8b13a7a..ae407994cdc6b 100644 --- a/.gitignore +++ b/.gitignore @@ -15,5 +15,6 @@ y.output profile.coverprofile explain_test cmd/explaintest/explain-test.out +cmd/explaintest/explaintest_tidb-server _tools/ *.fail.go diff --git a/executor/join.go b/executor/join.go index 205127e64ee55..0ea7bd0a39271 100644 --- a/executor/join.go +++ b/executor/join.go @@ -211,8 +211,11 @@ func (e *HashJoinExec) fetchOuterChunks(ctx context.Context) { } return } - if !hasWaitedForInner { + if outerResult.NumRows() == 0 { + e.finished.Store(true) + return + } jobFinished, innerErr := e.wait4Inner() if innerErr != nil { e.joinResultCh <- &hashjoinWorkerResult{ @@ -241,7 +244,7 @@ func (e *HashJoinExec) wait4Inner() (finished bool, err error) { return false, errors.Trace(err) } } - if e.hashTable.Len() == 0 && e.joinType == plannercore.InnerJoin { + if e.hashTable.Len() == 0 && (e.joinType == plannercore.InnerJoin || e.joinType == plannercore.SemiJoin) { return true, nil } return false, nil diff --git a/executor/join_test.go b/executor/join_test.go index 681687d9c1373..b1e79dd88c1e5 100644 --- a/executor/join_test.go +++ b/executor/join_test.go @@ -921,3 +921,62 @@ func (s *testSuite) TestEmbeddedOuterJoin(c *C) { tk.MustQuery("select * from (t1 left join t2 on t1.a = t2.a) left join (t2 t3 left join t2 t4 on t3.a = t4.a) on t2.b = 1"). Check(testkit.Rows("1 1 ")) } + +func (s *testSuite) TestHashJoin(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 int, b int);") + tk.MustExec("create table t2(a int, b int);") + tk.MustExec("insert into t1 values(1,1),(2,2),(3,3),(4,4),(5,5);") + tk.MustQuery("select count(*) from t1").Check(testkit.Rows("5")) + tk.MustQuery("select count(*) from t2").Check(testkit.Rows("0")) + tk.MustExec("set @@tidb_max_chunk_size=1;") + result := tk.MustQuery("explain analyze select /*+ TIDB_HJ(t1, t2) */ * from t1 where exists (select a from t2 where t1.a = t2.a);") + // id count task operator info execution info + // HashLeftJoin_9 8000.00 root semi join, inner:TableReader_13, equal:[eq(test.t1.a, test.t2.a)] time:1.036712ms, loops:1, rows:0 + // ├─TableReader_11 10000.00 root data:TableScan_10 time:441.096µs, loops:1, rows:1 + // │ └─TableScan_10 10000.00 cop table:t1, range:[-inf,+inf], keep order:false, stats:pseudo + // └─TableReader_13 10000.00 root data:TableScan_12 time:212.376µs, loops:1, rows:0 + // └─TableScan_12 10000.00 cop table:t2, range:[-inf,+inf], keep order:false, stats:pseudo + row := result.Rows() + c.Assert(len(row), Equals, 5) + outerExecInfo := row[1][4].(string) + c.Assert(outerExecInfo[len(outerExecInfo)-1:], Equals, "1") + innerExecInfo := row[3][4].(string) + c.Assert(innerExecInfo[len(innerExecInfo)-1:], Equals, "0") + + tk.MustExec("insert into t2 select * from t1;") + tk.MustExec("delete from t1;") + tk.MustQuery("select count(*) from t1").Check(testkit.Rows("0")) + tk.MustQuery("select count(*) from t2").Check(testkit.Rows("5")) + result = tk.MustQuery("explain analyze select /*+ TIDB_HJ(t1, t2) */ * from t1 where not exists (select a from t2 where t1.a = t2.a);") + // id count task operator info execution info | + // Projection_8 4.00 root test.t1.a, test.t1.b time:193.08µs, loops:1, rows:0 | + // └─Selection_9 4.00 root not(6_aux_0) time:146.95µs, loops:1, rows:0 | + // └─HashLeftJoin_10 5.00 root left outer semi join, inner:TableReader_14, equal:[eq(test.t1.a, test.t2.a)] time:144.293µs, loops:1, rows:0 | + // ├─TableReader_12 5.00 root data:TableScan_11 time:26.27µs, loops:1, rows:0 | + // │ └─TableScan_11 5.00 cop table:t1, range:[-inf,+inf], keep order:false, stats:pseudo | + // └─TableReader_14 5.00 root data:TableScan_13 time:0s, loops:0, rows:0 | + // └─TableScan_13 5.00 cop table:t2, range:[-inf,+inf], keep order:false, stats:pseudo | + row = result.Rows() + c.Assert(len(row), Equals, 7) + outerExecInfo = row[3][4].(string) + c.Assert(outerExecInfo[len(outerExecInfo)-1:], Equals, "0") + innerExecInfo = row[5][4].(string) + c.Assert(innerExecInfo[len(innerExecInfo)-1:], LessEqual, "5") + + result = tk.MustQuery("explain analyze select /*+ TIDB_HJ(t1, t2) */ * from t1 left outer join t2 on t1.a = t2.a;") + // id count task operator info execution info + // HashLeftJoin_6 12500.00 root left outer join, inner:TableReader_10, equal:[eq(test.t1.a, test.t2.a)] time:502.553µs, loops:1, rows:0 + // ├─TableReader_8 10000.00 root data:TableScan_7 time:27.302µs, loops:1, rows:0 + // │ └─TableScan_7 10000.00 cop table:t1, range:[-inf,+inf], keep order:false, stats:pseudo + // └─TableReader_10 10000.00 root data:TableScan_9 time:0s, loops:0, rows:0 + // └─TableScan_9 10000.00 cop table:t2, range:[-inf,+inf], keep order:false, stats:pseudo + row = result.Rows() + c.Assert(len(row), Equals, 5) + outerExecInfo = row[1][4].(string) + c.Assert(outerExecInfo[len(outerExecInfo)-1:], Equals, "0") + innerExecInfo = row[3][4].(string) + c.Assert(innerExecInfo[len(innerExecInfo)-1:], LessEqual, "5") +}