Files
tidb/executor/parallel_apply_test.go

638 lines
32 KiB
Go

// Copyright 2020 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package executor_test
import (
"fmt"
"strings"
"testing"
"github.com/pingcap/failpoint"
"github.com/pingcap/tidb/sessionctx/variable"
"github.com/pingcap/tidb/testkit"
"github.com/pingcap/tidb/util/collate"
"github.com/stretchr/testify/require"
)
func checkApplyPlan(t *testing.T, tk *testkit.TestKit, sql string, parallel int) {
results := tk.MustQuery("explain analyze " + sql)
containApply := false
for _, row := range results.Rows() {
line := fmt.Sprintf("%v", row)
if strings.Contains(line, "Apply") {
if parallel > 0 { // check the concurrency if parallel is larger than 0
str := "Concurrency:"
if parallel > 1 {
str += fmt.Sprintf("%v", parallel)
}
require.Contains(t, line, str)
}
containApply = true
break
}
}
require.True(t, containApply)
}
func TestParallelApplyPlan(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("create table t (a int, b int)")
tk.MustExec("insert into t values (0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9), (null, null)")
q1 := "select t1.b from t t1 where t1.b > (select max(b) from t t2 where t1.a > t2.a)"
checkApplyPlan(t, tk, q1, 0)
tk.MustQuery(q1).Sort().Check(testkit.Rows("1", "2", "3", "4", "5", "6", "7", "8", "9"))
tk.MustExec("set tidb_enable_parallel_apply=true")
checkApplyPlan(t, tk, q1, 1)
tk.MustQuery(q1).Sort().Check(testkit.Rows("1", "2", "3", "4", "5", "6", "7", "8", "9"))
q2 := "select * from t t0 where t0.b <= (select max(t1.b) from t t1 where t1.b > (select max(b) from t t2 where t1.a > t2.a and t0.a > t2.a));"
checkApplyPlan(t, tk, q2, 1) // only the outside apply can be parallel
tk.MustQuery(q2).Sort().Check(testkit.Rows("1 1", "2 2", "3 3", "4 4", "5 5", "6 6", "7 7", "8 8", "9 9"))
q3 := "select t1.b from t t1 where t1.b > (select max(b) from t t2 where t1.a > t2.a) order by t1.a"
checkApplyPlan(t, tk, q3, 0)
tk.MustExec("alter table t add index idx(a)")
checkApplyPlan(t, tk, q3, 1)
tk.MustQuery(q3).Sort().Check(testkit.Rows("1", "2", "3", "4", "5", "6", "7", "8", "9"))
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 Parallel Apply rejects the possible order properties of its outer child currently"))
}
func TestApplyColumnType(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("set tidb_enable_parallel_apply=true")
// int
tk.MustExec("create table t(a int, b int)")
tk.MustExec("create table t1(a int, b int)")
tk.MustExec("insert into t values(1,1), (2,2), (5,5), (2, 4), (5, 2), (9, 4)")
tk.MustExec("insert into t1 values(2, 3), (4, 9), (10, 4), (1, 10)")
sql := "select * from t where t.b > (select min(t1.b) from t1 where t1.a > t.a)"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("5 5"))
// varchar
tk.MustExec("drop table t, t1")
tk.MustExec("create table t1(a varchar(255), b varchar(255))")
tk.MustExec("create table t2(a varchar(255))")
tk.MustExec(`insert into t1 values("aa", "bb"), ("aa", "tikv"), ("bb", "cc"), ("bb", "ee")`)
tk.MustExec(`insert into t2 values("kk"), ("aa"), ("dd"), ("bb")`)
sql = "select (select min(t2.a) from t2 where t2.a > t1.a) from t1"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("bb", "bb", "dd", "dd"))
// bit
tk.MustExec("drop table t1, t2")
tk.MustExec("create table t1(a bit(10), b int)")
tk.MustExec("create table t2(a bit(10), b int)")
tk.MustExec(`insert into t1 values ('1', 1), ('2', 2), ('3', 3), ('4', 4), ('1', 1), ('2', 2), ('3', 3), ('4', 4)`)
tk.MustExec(`insert into t2 values ('1', 1), ('2', 2), ('3', 3), ('4', 4), ('1', 1), ('2', 2), ('3', 3), ('4', 4)`)
sql = "select b from t1 where t1.b > (select min(t2.b) from t2 where t2.a < t1.a)"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("2", "2", "3", "3", "4", "4"))
// char
tk.MustExec("drop table t1, t2")
tk.MustExec("create table t1(a char(25), b int)")
tk.MustExec("create table t2(a char(10), b int)")
tk.MustExec(`insert into t1 values("abc", 1), ("abc", "5"), ("fff", 4), ("fff", 9), ("tidb", 6), ("tidb", 5)`)
tk.MustExec(`insert into t2 values()`)
sql = "select t1.b from t1 where t1.b > (select max(t2.b) from t2 where t2.a > t1.a)"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows())
// double
tk.MustExec("drop table t1, t2")
tk.MustExec("create table t1(a int, b double)")
tk.MustExec("create table t2(a int, b double)")
tk.MustExec("insert into t1 values(1, 2.12), (1, 1.11), (2, 3), (2, 4.56), (5, 55), (5, -4)")
tk.MustExec("insert into t2 values(1, 3.22), (3, 4.5), (5, 2.3), (4, 5.55)")
sql = "select * from t1 where t1.a < (select avg(t2.a) from t2 where t2.b > t1.b)"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("1 1.11", "1 2.12", "2 3", "2 4.56"))
// date
tk.MustExec("drop table t1, t2")
tk.MustExec("create table t1(a date, b int, c int)")
tk.MustExec("create table t2(a date, b int)")
tk.MustExec(`insert into t1 values("2020-01-01", 3, 4), ("2020-01-01", 4, 5), ("2020-01-01", 4, 3), ("2020-02-01", 7, 7), ("2020-02-01", 6, 6)`)
tk.MustExec(`insert into t2 values("2020-01-02", 4), ("2020-02-02", 8), ("2020-02-02", 7)`)
sql = "select * from t1 where t1.b >= (select min(t2.b) from t2 where t2.a > t1.a) and t1.c >= (select min(t2.b) from t2 where t2.a > t1.a)"
tk.MustQuery(sql).Sort().Check(testkit.Rows("2020-01-01 4 5", "2020-02-01 7 7"))
// datetime
tk.MustExec("drop table t1, t2")
tk.MustExec("create table t1(a datetime, b int)")
tk.MustExec("create table t2(a datetime, b int)")
tk.MustExec(`insert into t1 values("2020-01-01 00:00:00", 1), ("2020-01-01 00:00:00", 2), ("2020-06-06 00:00:00", 3), ("2020-06-06 00:00:00", 4), ("2020-09-08 00:00:00", 4)`)
tk.MustExec(`insert into t2 values("2020-01-01 00:00:00", 1), ("2020-01-01 00:00:01", 2), ("2020-08-20 00:00:00", 4)`)
sql = "select b from t1 where t1.b >= (select max(t2.b) from t2 where t2.a > t1.a)"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("4"))
// timestamp
tk.MustExec("drop table t1, t2")
tk.MustExec("create table t1(a timestamp, b int)")
tk.MustExec("create table t2(a timestamp, b int)")
tk.MustExec(`insert into t1 values("2020-01-01 00:00:00", 1), ("2020-01-01 00:00:00", 2), ("2020-06-06 00:00:00", 3), ("2020-06-06 00:00:00", 4), ("2020-09-08 00:00:00", 4)`)
tk.MustExec(`insert into t2 values("2020-01-01 00:00:00", 1), ("2020-01-01 00:00:01", 2), ("2020-08-20 00:00:00", 4)`)
sql = "select b from t1 where t1.b >= (select max(t2.b) from t2 where t2.a > t1.a)"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("4"))
}
func TestApplyMultiColumnType(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("set tidb_enable_parallel_apply=true")
// int & int
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), (1, 1), (2, 2), (2, 3), (2, 3), (1, 1), (1, 1), (2, 2), (2, 3), (2, 3)")
tk.MustExec("insert into t2 values (2, 2), (3,3), (-1, 1), (5, 4), (2, 2), (3,3), (-1, 1), (5, 4)")
sql := "select (select count(*) from t2 where t2.a > t1.a and t2.b > t1.a) from t1"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("4", "4", "4", "4", "4", "4", "6", "6", "6", "6"))
// int & char
tk.MustExec("drop table t1, t2")
tk.MustExec("create table t1(a int, b char(20))")
tk.MustExec("create table t2(a int, b char(20))")
tk.MustExec(`insert into t1 values (1, "a"), (2, "b"), (3, "c"), (1, "a"), (2, "b"), (3, "c")`)
tk.MustExec(`insert into t2 values (1, "a"), (2, "b"), (3, "c"), (1, "a"), (2, "b"), (3, "c")`)
sql = "select (select sum(t2.a) from t2 where t2.a > t1.a or t2.b < t1.b) from t1"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("10", "10", "6", "6", "8", "8"))
// int & bit
tk.MustExec("drop table t1, t2")
tk.MustExec("create table t1(a int, b bit(10), c int)")
tk.MustExec("create table t2(a int, b int, c int)")
tk.MustExec(`insert into t1 values (1, '1', 1), (2, '2', 4), (3, '3', 6), (4, '4', 8), (1, '1', 1), (2, '2', 4), (3, '3', 6), (4, '4', 8)`)
tk.MustExec(`insert into t2 values (1, 1111, 11), (2, 2222, 22), (1, 1111, 11), (2, 2222, 22)`)
sql = "select a, c from t1 where (select max(t2.c) from t2 where t2.a > t1.a and t2.b > t1.b) > 4"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("1 1", "1 1"))
// char & char
tk.MustExec("drop table t1, t2")
tk.MustExec("create table t1(a char(20), b varchar(255))")
tk.MustExec("create table t2(a char(20), b varchar(255))")
tk.MustExec(`insert into t1 values ('7', '7'), ('8', '8'), ('9', '9'), ('7', '7'), ('8', '8'), ('9', '9')`)
tk.MustExec(`insert into t2 values ('7', '7'), ('8', '8'), ('9', '9'), ('7', '7'), ('8', '8'), ('9', '9')`)
sql = "select count(*) from t1 where (select sum(t2.a) from t2 where t2.a >= t1.a and t2.b >= t1.b) > 4"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("6"))
// enum & char
tk.MustExec("drop table t1, t2")
tk.MustExec(`create table t1(a varchar(20), b enum("a", "b", "c", "d", "e","f"))`)
tk.MustExec("create table t2(a varchar(20), b int)")
tk.MustExec(`insert into t1 values ('1', 'a'), ('2', 'b'), ('3', 'c'), ('1', 'a'), ('2', 'b'), ('3', 'c')`)
tk.MustExec(`insert into t2 values ('1', 100), ('2', 200), ('3', 300), ('4', 400), ('1', 100), ('2', 200), ('3', 300), ('4', 400)`)
sql = "select * from t1 where (select sum(t2.b) from t2 where t2.a > t1.a and t2.b * 2 > t1.b) > 0"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("1 a", "1 a", "2 b", "2 b", "3 c", "3 c"))
// char & bit
tk.MustExec("drop table t1, t2")
tk.MustExec("create table t1(a varchar(20), b bit(10))")
tk.MustExec("create table t2(a varchar(20), b int)")
tk.MustExec("insert into t1 values ('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('1', '1'), ('2', '2'), ('3', '3'), ('4', '4')")
tk.MustExec("insert into t2 values ('1', 1), ('2', 2), ('3', 3), ('4', 4), ('1', 1), ('2', 2), ('3', 3), ('4', 4)")
sql = "select a from t1 where (select sum(t2.b) from t2 where t2.a > t1.a and t2.b < t1.b) > 4"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("1", "1", "2", "2", "3", "3"))
// int & double
tk.MustExec("drop table t1, t2")
tk.MustExec("create table t1 (a int, b double)")
tk.MustExec("create table t2 (a int, b double)")
tk.MustExec("insert into t1 values (1, 1.1), (2, 2.2), (3, 3.3), (4, 4.4), (1, 1.1), (2, 2.2), (3, 3.3), (4, 4.4)")
tk.MustExec("insert into t2 values (1, 1.1), (2, 2.2), (3, 3.3), (4, 4.4), (1, 1.1), (2, 2.2), (3, 3.3), (4, 4.4)")
sql = "select * from t1 where (select min(t2.a) from t2 where t2.a < t1.a and t2.a > 1 and t2.b < t1.b) > 0"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("3 3.3", "3 3.3", "4 4.4", "4 4.4"))
// int & datetime
tk.MustExec("drop table t1, t2")
tk.MustExec("create table t1(a int, b datetime)")
tk.MustExec("create table t2(a int, b datetime)")
tk.MustExec(`insert into t1 values (1, "2020-01-01"), (2, "2020-02-02"), (3, "2020-03-03"), (1, "2020-01-01"), (2, "2020-02-02"), (3, "2020-03-03")`)
tk.MustExec(`insert into t2 values (1, "2020-01-01"), (2, "2020-02-02"), (3, "2020-03-03"), (1, "2020-01-01"), (2, "2020-02-02"), (3, "2020-03-03")`)
sql = `select * from t1 where (select count(*) from t2 where t2.a >= t1.a and t2.b between t1.b and "2020-09-07 00:00:00") > 1`
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("1 2020-01-01 00:00:00", "1 2020-01-01 00:00:00", "2 2020-02-02 00:00:00", "2 2020-02-02 00:00:00", "3 2020-03-03 00:00:00", "3 2020-03-03 00:00:00"))
// int & int & char
tk.MustExec("drop table t1, t2")
tk.MustExec("create table t1(a int, b int, c varchar(20))")
tk.MustExec("create table t2(a int, b int, c varchar(20))")
tk.MustExec("insert into t1 values (1, 1, '1'), (2, 2, '2'), (3, 3, '3'), (1, 1, '1'), (2, 2, '2'), (3, 3, '3')")
tk.MustExec("insert into t2 values (1, 1, '1'), (2, 2, '2'), (3, 3, '3'), (1, 1, '1'), (2, 2, '2'), (3, 3, '3')")
sql = "select (select min(t2.a) from t2 where t2.a > t1.a and t2.b > t1.b and t2.c > t1.c) from t1"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("2", "2", "3", "3", "<nil>", "<nil>"))
}
func TestSetTiDBEnableParallelApply(t *testing.T) {
// validate the tidb_enable_parallel_apply's value
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("set tidb_enable_parallel_apply=0")
tk.MustQuery("select @@tidb_enable_parallel_apply").Check(testkit.Rows("0"))
tk.MustExec("set tidb_enable_parallel_apply=1")
tk.MustQuery("select @@tidb_enable_parallel_apply").Check(testkit.Rows("1"))
tk.MustExec("set tidb_enable_parallel_apply=on")
tk.MustQuery("select @@tidb_enable_parallel_apply").Check(testkit.Rows("1"))
tk.MustExec("set tidb_enable_parallel_apply=off")
tk.MustQuery("select @@tidb_enable_parallel_apply").Check(testkit.Rows("0"))
require.Error(t, tk.ExecToErr("set tidb_enable_parallel_apply=-1"))
require.Error(t, tk.ExecToErr("set tidb_enable_parallel_apply=2"))
require.Error(t, tk.ExecToErr("set tidb_enable_parallel_apply=1000"))
require.Error(t, tk.ExecToErr("set tidb_enable_parallel_apply='onnn'"))
}
func TestMultipleApply(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("set tidb_enable_parallel_apply=true")
// compare apply with constant values
tk.MustExec("drop table if exists t1, t2")
tk.MustExec(`create table t1(a varchar(20), b enum("a", "b", "c", "d", "e","f"))`)
tk.MustExec("create table t2(a varchar(20), b int)")
tk.MustExec(`insert into t1 values ("1", "a"), ("2", "b"), ("3", "c"), ("4", "d"), ("1", "a"), ("2", "b"), ("3", "c"), ("4", "d")`)
tk.MustExec(`insert into t2 values ("1", 1), ("2", 2), ("3", 3), ("4", 4), ("1", 1), ("2", 2), ("3", 3), ("4", 4)`)
sql := "select * from t1 where (select sum(t2.b) from t2 where t2.a > t1.a) >= (select sum(t2.b) from t2 where t2.b > t1.b)"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("1 a", "1 a", "2 b", "2 b", "3 c", "3 c"))
// 2 apply operators in where conditions
tk.MustExec("drop table if exists t1, t2")
tk.MustExec("create table t1(a int, b double)")
tk.MustExec("create table t2(a int, b double)")
tk.MustExec("insert into t1 values (1, 1.1), (2, 2.2), (3, 3.3), (4, 4.4), (1, 1.1), (2, 2.2), (3, 3.3), (4, 4.4)")
tk.MustExec("insert into t2 values (1, 1.1), (2, 2.2), (3, 3.3), (4, 4.4), (1, 1.1), (2, 2.2), (3, 3.3), (4, 4.4)")
sql = "select * from t1 where (select min(t2.a) from t2 where t2.a < t1.a and t2.a > 1) * (select min(t2.a) from t2 where t2.b < t1.b) > 1"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("3 3.3", "3 3.3", "4 4.4", "4 4.4"))
// 2 apply operators and compare it with constant values
tk.MustExec("drop table if exists t1, t2")
tk.MustExec("create table t1(a varchar(20), b bit(10))")
tk.MustExec("create table t2(a varchar(20), b int)")
tk.MustExec("insert into t1 values ('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('1', '1'), ('2', '2'), ('3', '3'), ('4', '4')")
tk.MustExec("insert into t2 values ('1', 1111), ('2', 2222), ('3', 3333), ('4', 4444), ('1', 1111), ('2', 2222), ('3', 3333), ('4', 4444)")
sql = "select a from t1 where (select sum(t2.b) from t2 where t2.a > t1.a) > 4 and (select sum(t2.b) from t2 where t2.b > t1.b) > 4"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("1", "1", "2", "2", "3", "3"))
// multiple fields and where conditions
tk.MustExec("drop table if exists t1, t2")
tk.MustExec("create table t1(a int, b int, c varchar(20))")
tk.MustExec("create table t2(a int, b int, c varchar(20))")
tk.MustExec("insert into t1 values (1, 1, '1'), (2, 2, '2'), (3, 3, '3'), (4, 4, '4'), (1, 1, '1'), (2, 2, '2'), (3, 3, '3'), (4, 4, '4')")
tk.MustExec("insert into t2 values (1, 1, '1'), (2, 2, '2'), (3, 3, '3'), (4, 4, '4'), (1, 1, '1'), (2, 2, '2'), (3, 3, '3'), (4, 4, '4')")
sql = "select (select min(t2.a) from t2 where t2.a > t1.a and t2.b > t1.b), (select max(t2.a) from t2 where t2.a > t1.a and t2.b > t1.b) from t1 where (select count(*) from t2 where t2.c > t1.c) > 3"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("2 4", "2 4", "3 4", "3 4"))
}
func TestApplyWithOtherOperators(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("set tidb_enable_parallel_apply=true")
// hash join
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), (1, 1), (2, 2), (3, 3)")
tk.MustExec("insert into t2 values (1, 1), (2, 2), (3, 3), (1, 1), (2, 2), (3, 3)")
sql := "select /*+ hash_join(t1) */ (select count(t2.b) from t2 where t1.a > t2.a) from t1, t2 where t1.a = t2.a"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("0", "0", "0", "0", "2", "2", "2", "2", "4", "4", "4", "4"))
// merge join
tk.MustExec("drop table if exists t1, t2")
tk.MustExec("create table t1(a double, b int)")
tk.MustExec("create table t2(a int, b int)")
tk.MustExec("insert into t1 values (1, 1), (2, 2), (3, 3), (1, 1), (2, 2), (3, 3)")
tk.MustExec("insert into t2 values (1, 1), (2, 2), (3, 3), (1, 1), (2, 2), (3, 3)")
sql = "select /*+ merge_join(t1) */ (select count(t2.b) from t2 where t1.a > t2.a) from t1, t2 where t1.a = t2.a"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("0", "0", "0", "0", "2", "2", "2", "2", "4", "4", "4", "4"))
// index merge join
tk.MustExec("drop table if exists t1, t2")
tk.MustExec("create table t1(a int primary key, b int)")
tk.MustExec("create table t2(a int, b int, index idx(a))")
tk.MustExec("insert into t1 values (1, 1), (2, 2), (3, 3)")
tk.MustExec("insert into t2 values (1, 1), (2, 2), (3, 3), (1, 1), (2, 2), (3, 3)")
sql = "select /*+ inl_merge_join(t1) */ (select count(t2.b) from t2 where t1.a > t2.a) from t1, t2 where t1.a = t2.a"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("0", "0", "2", "2", "4", "4"))
sql = "select /*+ inl_merge_join(t2) */ (select count(t2.b) from t2 where t1.a > t2.a) from t1, t2 where t1.a = t2.a"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("0", "0", "2", "2", "4", "4"))
// index hash join
tk.MustExec("drop table if exists t1, t2")
tk.MustExec("create table t1(a int, b int, index idx(a, b))")
tk.MustExec("create table t2(a int, b int, index idx(a))")
tk.MustExec("insert into t1 values (1, 1), (2, 2), (3, 3), (1, 1), (2, 2), (3, 3)")
tk.MustExec("insert into t2 values (1, 1), (2, 2), (3, 3), (1, 1), (2, 2), (3, 3)")
sql = "select /*+ inl_hash_join(t1) */ (select count(t2.b) from t2 where t1.a > t2.a) from t1, t2 where t1.a = t2.a"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("0", "0", "0", "0", "2", "2", "2", "2", "4", "4", "4", "4"))
sql = "select /*+ inl_hash_join(t2) */ (select count(t2.b) from t2 where t1.a > t2.a) from t1, t2 where t1.a = t2.a"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("0", "0", "0", "0", "2", "2", "2", "2", "4", "4", "4", "4"))
// index join
tk.MustExec("drop table if exists t1, t2")
tk.MustExec("create table t1(a int , b int, unique index idx(a))")
tk.MustExec("create table t2(a int, b int, unique index idx(a))")
tk.MustExec("insert into t1 values (1, 1), (2, 2), (3, 3)")
tk.MustExec("insert into t2 values (1, 1), (2, 2), (3, 3)")
sql = "select /*+ inl_join(t1) */ (select count(t2.b) from t2 where t1.a > t2.a) from t1, t2 where t1.a = t2.a"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("0", "1", "2"))
sql = "select /*+ inl_join(t2) */ (select count(t2.b) from t2 where t1.a > t2.a) from t1, t2 where t1.a = t2.a"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("0", "1", "2"))
// index merge
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(a int, b int, c int, index idxa(a), unique index idxb(b))")
tk.MustExec("insert into t values (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (1, 5, 1), (2, 6, 2), (3, 7, 3), (4, 8, 4)")
sql = "select /*+ use_index_merge(t) */ * from t where (a > 0 or b < 0) and (select count(*) from t t1 where t1.c > t.a) > 0"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("1 1 1", "1 5 1", "2 2 2", "2 6 2", "3 3 3", "3 7 3"))
// aggregation
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(a int, b int)")
tk.MustExec("insert into t values (1, 1), (2, 2), (3, 3), (4, 4), (1, 1), (2, 2), (3, 3), (4, 4)")
sql = "select /*+ stream_agg() */ a from t where (select count(*) from t1 where t1.b > t.a) > 1 group by a"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("1"))
sql = "select /*+ hash_agg() */ a from t where (select count(*) from t1 where t1.b > t.a) > 1 group by a"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("1"))
}
func TestApplyWithOtherFeatures(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("set tidb_enable_parallel_apply=true")
// collation 1
tk.MustExec("drop table if exists t, t1")
tk.MustExec("create table t(a varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci, b int)")
tk.MustExec("create table t1(a varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci, b int)")
tk.MustExec("insert into t values ('a', 1), ('A', 2), ('a', 3), ('A', 4)")
tk.MustExec("insert into t1 values ('a', 1), ('A', 2), ('a', 3), ('A', 4)")
sql := "select (select min(t1.b) from t1 where t1.a >= t.a), (select sum(t1.b) from t1 where t1.a >= t.a) from t"
tk.MustQuery(sql).Sort().Check(testkit.Rows("1 10", "1 10", "1 10", "1 10"))
// collation 2
sql = "select (select min(t1.b) from t1 where t1.a >= t.a and t1.b >= t.b), (select sum(t1.b) from t1 where t1.a >= t.a and t1.b >= t.b) from t"
tk.MustQuery(sql).Sort().Check(testkit.Rows("1 10", "2 9", "3 7", "4 4"))
collate.SetNewCollationEnabledForTest(false)
defer collate.SetNewCollationEnabledForTest(true)
// plan cache
tk.MustExec(`set tidb_enable_prepared_plan_cache=1`)
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), (1, 5), (2, 3), (2, 4), (3, 3)")
tk.MustExec("insert into t2 values (0, 1), (2, -1), (3, 2)")
tk.MustExec(`prepare stmt from "select * from t1 where t1.b >= (select sum(t2.b) from t2 where t2.a > t1.a and t2.a > ?)"`)
tk.MustExec("set @a=1")
tk.MustQuery("execute stmt using @a").Sort().Check(testkit.Rows("1 1", "1 5", "2 3", "2 4"))
tk.MustExec("set @a=2")
tk.MustQuery("execute stmt using @a").Sort().Check(testkit.Rows("1 5", "2 3", "2 4"))
tk.MustQuery(" select @@last_plan_from_cache").Check(testkit.Rows("0")) // sub-queries are not cacheable
// cluster index
tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn
tk.MustExec("drop table if exists t, t2")
tk.MustExec("create table t(a int, b int, c int, primary key(a, b))")
tk.MustExec("create table t2(a int, b int, c int, primary key(a, c))")
tk.MustExec("insert into t values (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4)")
tk.MustExec("insert into t2 values (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4)")
sql = "select * from t where (select min(t2.b) from t2 where t2.a > t.a) > 0"
tk.MustQuery(sql).Sort().Check(testkit.Rows("1 1 1", "2 2 2", "3 3 3"))
tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeIntOnly
// partitioning table
tk.MustExec("drop table if exists t1, t2")
tk.MustExec("create table t1(a int, b int) partition by range(a) (partition p0 values less than(10), partition p1 values less than(20), partition p2 values less than(30), partition p3 values less than(40))")
tk.MustExec("create table t2(a int, b int) partition by hash(a) partitions 4")
tk.MustExec("insert into t1 values (5, 5), (15, 15), (25, 25), (35, 35)")
tk.MustExec("insert into t2 values (5, 5), (15, 15), (25, 25), (35, 35)")
sql = "select (select count(*) from t2 where t2.a > t1.b and t2.a=20), (select max(t2.b) from t2 where t2.a between t1.a and 20) from t1 where t1.a > 10"
tk.MustQuery(sql).Sort().Check(testkit.Rows("0 15", "0 <nil>", "0 <nil>"))
}
func TestApplyInDML(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("set tidb_enable_parallel_apply=true")
// delete
tk.MustExec("drop table if exists t, t2")
tk.MustExec("create table t(a bigint, b int)")
tk.MustExec("create table t2(a int, b int)")
tk.MustExec("insert into t values (1, 1), (2, 2), (3, 3), (4, 4), (1, 1), (2, 2), (3, 3), (4, 4)")
tk.MustExec("insert into t2 values (1, 1), (2, 2), (3, 3), (4, 4), (1, 1), (2, 2), (3, 3), (4, 4)")
tk.MustExec("delete from t where (select min(t2.a) * 2 from t2 where t2.a < t.a) > 1")
tk.MustQuery("select * from t").Sort().Check(testkit.Rows("1 1", "1 1"))
// insert
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(a int, b int, c int)")
tk.MustExec("insert into t values (1, 1, 1), (2, 2, 2), (3, 3, 3), (1, 1, 1), (2, 2, 2), (3, 3, 3)")
tk.MustExec("insert into t (select * from t where (select count(*) from t t1 where t1.b > t.a) > 2)")
tk.MustQuery("select * from t").Sort().Check(testkit.Rows("1 1 1", "1 1 1", "1 1 1", "1 1 1", "2 2 2", "2 2 2", "3 3 3", "3 3 3"))
// update
tk.MustExec("drop table if exists t, t2")
tk.MustExec("create table t(a smallint, b int)")
tk.MustExec("create table t2(a int, b int)")
tk.MustExec("insert into t values (1, 1), (2, 2), (3, 3), (1, 1), (2, 2), (3, 3)")
tk.MustExec("insert into t2 values (1, 1), (2, 2), (3, 3), (1, 1), (2, 2), (3, 3)")
tk.MustExec("update t set a = a + 1 where (select count(*) from t2 where t2.a <= t.a) in (1, 2)")
tk.MustQuery("select * from t").Sort().Check(testkit.Rows("2 1", "2 1", "2 2", "2 2", "3 3", "3 3"))
// replace
tk.MustExec("drop table if exists t, t2")
tk.MustExec("create table t(a tinyint, b int, unique index idx(a))")
tk.MustExec("create table t2(a tinyint, b int)")
tk.MustExec("insert into t values (1, 1), (2, 2), (3, 3), (4, 4)")
tk.MustExec("insert into t2 values (1, 1), (2, 2), (3, 3), (1, 1), (2, 2), (3, 3)")
tk.MustExec("replace into t (select pow(t2.a, 2), t2.b from t2 where (select min(t.a) from t where t.a > t2.a) between 1 and 5)")
tk.MustQuery("select * from t").Sort().Check(testkit.Rows("1 1", "2 2", "3 3", "4 2", "9 3"))
// Transaction
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, 2), (1, 3)")
tk.MustExec("begin")
tk.MustExec("insert into t1 values (1, 4), (2, 3), (2, 5)")
tk.MustExec("insert into t2 values (2, 3), (3, 4)")
sql := "select * from t1 where t1.b > any (select t2.b from t2 where t2.b < t1.b)"
tk.MustQuery(sql).Sort().Check(testkit.Rows("1 4", "2 5"))
tk.MustExec("delete from t1 where a = 1")
tk.MustQuery(sql).Sort().Check(testkit.Rows("2 5"))
tk.MustExec("commit")
tk.MustQuery(sql).Sort().Check(testkit.Rows("2 5"))
}
func TestApplyConcurrency(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("set tidb_enable_parallel_apply=true")
// tidb_executor_concurrency
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)")
sql := "select * from t1 where t1.b > (select sum(t2.b) from t2 where t2.a > t1.a)"
tk.MustExec("set tidb_executor_concurrency = 3")
checkApplyPlan(t, tk, sql, 3)
tk.MustExec("set tidb_executor_concurrency = 5")
checkApplyPlan(t, tk, sql, 5)
// concurrency
tk.MustExec("drop table if exists t")
tk.MustExec("create table t (a int)")
vals := ""
n := 100
for i := 1; i <= n; i++ {
if i > 1 {
vals += ","
}
vals = vals + fmt.Sprintf("(%v)", i)
}
tk.MustExec(fmt.Sprintf("insert into t values %v", vals))
sql = "select sum(a) from t where t.a >= (select max(a) from t t1 where t1.a <= t.a)"
for cc := 1; cc <= 10; cc += 3 {
tk.MustExec(fmt.Sprintf("set tidb_executor_concurrency = %v", cc))
tk.MustQuery(sql).Check(testkit.Rows(fmt.Sprintf("%v", (n*(n+1))/2)))
}
}
func TestApplyCacheRatio(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, 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)")
sql := "select * from t1 where (select min(t2.b) from t2 where t2.a> t1.a) > 10"
tk.MustExec("insert into t2 values (1, 1), (2, 2), (3, 3), (4, 4), (5, 5)")
checkRatio := func(ratio string) bool {
rows := tk.MustQuery("explain analyze " + sql).Rows()
for _, r := range rows {
line := fmt.Sprintf("%v", r)
if strings.Contains(line, "cacheHitRatio:"+ratio) {
return true
}
}
return false
}
// 10%
tk.MustExec("insert into t1 values (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9), (1, 1)")
require.True(t, checkRatio("10.000%"))
tk.MustExec("set tidb_mem_quota_apply_cache = 0")
require.False(t, checkRatio(""))
tk.MustExec("set tidb_mem_quota_apply_cache = 33554432")
// 20%
tk.MustExec("truncate t1")
tk.MustExec("insert into t1 values (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (2, 2), (1, 1)")
require.True(t, checkRatio("20.000%"))
tk.MustExec("set tidb_mem_quota_apply_cache = 0")
require.False(t, checkRatio(""))
tk.MustExec("set tidb_mem_quota_apply_cache = 33554432")
// 50%
tk.MustExec("truncate t1")
tk.MustExec("insert into t1 values (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5)")
require.True(t, checkRatio("50.000%"))
tk.MustExec("set tidb_mem_quota_apply_cache = 0")
require.False(t, checkRatio(""))
}
func TestApplyGoroutinePanic(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("set tidb_enable_parallel_apply=true")
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), (1, 1), (2, 2), (2, 3), (2, 3), (1, 1), (1, 1), (2, 2), (2, 3), (2, 3)")
tk.MustExec("insert into t2 values (2, 2), (3,3), (-1, 1), (5, 4), (2, 2), (3,3), (-1, 1), (5, 4)")
// no panic
sql := "select (select count(*) from t2 where t2.a > t1.a and t2.b > t1.a) from t1"
checkApplyPlan(t, tk, sql, 1)
tk.MustQuery(sql).Sort().Check(testkit.Rows("4", "4", "4", "4", "4", "4", "6", "6", "6", "6"))
// panic in a inner worker
require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/parallelApplyInnerWorkerPanic", "panic"))
err := tk.QueryToErr(sql)
require.Error(t, err)
require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/parallelApplyInnerWorkerPanic"))
for _, panicName := range []string{"parallelApplyInnerWorkerPanic", "parallelApplyOuterWorkerPanic", "parallelApplyGetCachePanic", "parallelApplySetCachePanic"} {
panicPath := fmt.Sprintf("github.com/pingcap/tidb/executor/%v", panicName)
require.NoError(t, failpoint.Enable(panicPath, "panic"))
require.Error(t, tk.QueryToErr(sql))
require.NoError(t, failpoint.Disable(panicPath))
}
}
func TestIssue24930(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("set tidb_enable_parallel_apply=true")
tk.MustExec("drop table if exists t1, t2")
tk.MustExec("create table t1(a int)")
tk.MustExec("create table t2(a int)")
tk.MustQuery(`select case when t1.a is null
then (select t2.a from t2 where t2.a = t1.a limit 1) else t1.a end a
from t1 where t1.a=1 order by a limit 1`).Check(testkit.Rows()) // can return an empty result instead of hanging forever
}