From db8fa44665307b17ad867f2f97276d2405599630 Mon Sep 17 00:00:00 2001 From: senhtry Date: Wed, 22 Sep 2021 16:33:32 +0800 Subject: [PATCH 1/3] bindinfo: migrate test-infra to testify(staging-3) --- bindinfo/bind_test.go | 644 ---------------------------- bindinfo/capture_serial_test.go | 714 ++++++++++++++++++++++++++++++++ testkit/testkit.go | 12 + 3 files changed, 726 insertions(+), 644 deletions(-) create mode 100644 bindinfo/capture_serial_test.go diff --git a/bindinfo/bind_test.go b/bindinfo/bind_test.go index f988f8242a087..36100c294feb2 100644 --- a/bindinfo/bind_test.go +++ b/bindinfo/bind_test.go @@ -41,7 +41,6 @@ import ( "github.com/pingcap/tidb/util" "github.com/pingcap/tidb/util/logutil" utilparser "github.com/pingcap/tidb/util/parser" - "github.com/pingcap/tidb/util/stmtsummary" "github.com/pingcap/tidb/util/testkit" "github.com/pingcap/tidb/util/testleak" "github.com/tikv/client-go/v2/testutils" @@ -518,175 +517,6 @@ func (s *testSuite) TestErrorBind(c *C) { c.Check(chk.NumRows(), Equals, 0) } -func (s *testSuite) TestDMLCapturePlanBaseline(c *C) { - tk := testkit.NewTestKit(c, s.store) - s.cleanBindingEnv(tk) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec(" set @@tidb_capture_plan_baselines = on") - defer func() { - tk.MustExec(" set @@tidb_capture_plan_baselines = off") - }() - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, c int, key idx_b(b), key idx_c(c))") - tk.MustExec("create table t1 like t") - s.domain.BindHandle().CaptureBaselines() - tk.MustQuery("show global bindings").Check(testkit.Rows()) - tk.MustExec("delete from t where b = 1 and c > 1") - tk.MustExec("delete from t where b = 1 and c > 1") - tk.MustExec("update t set a = 1 where b = 1 and c > 1") - tk.MustExec("update t set a = 1 where b = 1 and c > 1") - tk.MustExec("insert into t1 select * from t where t.b = 1 and t.c > 1") - tk.MustExec("insert into t1 select * from t where t.b = 1 and t.c > 1") - tk.MustExec("replace into t1 select * from t where t.b = 1 and t.c > 1") - tk.MustExec("replace into t1 select * from t where t.b = 1 and t.c > 1") - tk.MustExec("insert into t1 values(1,1,1)") - tk.MustExec("insert into t1 values(1,1,1)") - tk.MustExec("replace into t1 values(1,1,1)") - tk.MustExec("replace into t1 values(1,1,1)") - tk.MustExec("admin capture bindings") - rows := tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 0) - - c.Assert(tk.Se.Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil), IsTrue) - tk.MustExec("delete from t where b = 1 and c > 1") - tk.MustExec("delete from t where b = 1 and c > 1") - tk.MustExec("update t set a = 1 where b = 1 and c > 1") - tk.MustExec("update t set a = 1 where b = 1 and c > 1") - tk.MustExec("insert into t1 select * from t where t.b = 1 and t.c > 1") - tk.MustExec("insert into t1 select * from t where t.b = 1 and t.c > 1") - tk.MustExec("replace into t1 select * from t where t.b = 1 and t.c > 1") - tk.MustExec("replace into t1 select * from t where t.b = 1 and t.c > 1") - tk.MustExec("insert into t1 values(1,1,1)") - tk.MustExec("insert into t1 values(1,1,1)") - tk.MustExec("replace into t1 values(1,1,1)") - tk.MustExec("replace into t1 values(1,1,1)") - tk.MustExec("admin capture bindings") - rows = tk.MustQuery("show global bindings").Sort().Rows() - c.Assert(len(rows), Equals, 4) - c.Assert(rows[0][0], Equals, "delete from `test` . `t` where `b` = ? and `c` > ?") - c.Assert(rows[0][1], Equals, "DELETE /*+ use_index(@`del_1` `test`.`t` `idx_b`)*/ FROM `test`.`t` WHERE `b` = 1 AND `c` > 1") - c.Assert(rows[1][0], Equals, "insert into `test` . `t1` select * from `test` . `t` where `t` . `b` = ? and `t` . `c` > ?") - c.Assert(rows[1][1], Equals, "INSERT INTO `test`.`t1` SELECT /*+ use_index(@`sel_1` `test`.`t` `idx_b`)*/ * FROM `test`.`t` WHERE `t`.`b` = 1 AND `t`.`c` > 1") - c.Assert(rows[2][0], Equals, "replace into `test` . `t1` select * from `test` . `t` where `t` . `b` = ? and `t` . `c` > ?") - c.Assert(rows[2][1], Equals, "REPLACE INTO `test`.`t1` SELECT /*+ use_index(@`sel_1` `test`.`t` `idx_b`)*/ * FROM `test`.`t` WHERE `t`.`b` = 1 AND `t`.`c` > 1") - c.Assert(rows[3][0], Equals, "update `test` . `t` set `a` = ? where `b` = ? and `c` > ?") - c.Assert(rows[3][1], Equals, "UPDATE /*+ use_index(@`upd_1` `test`.`t` `idx_b`)*/ `test`.`t` SET `a`=1 WHERE `b` = 1 AND `c` > 1") -} - -func (s *testSuite) TestCapturePlanBaseline(c *C) { - tk := testkit.NewTestKit(c, s.store) - s.cleanBindingEnv(tk) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec(" set @@tidb_capture_plan_baselines = on") - defer func() { - tk.MustExec(" set @@tidb_capture_plan_baselines = off") - }() - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int)") - s.domain.BindHandle().CaptureBaselines() - tk.MustQuery("show global bindings").Check(testkit.Rows()) - tk.MustExec("select count(*) from t where a > 10") - tk.MustExec("select count(*) from t where a > 10") - tk.MustExec("admin capture bindings") - rows := tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 0) - - c.Assert(tk.Se.Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil), IsTrue) - tk.MustExec("select * from t where a > 10") - tk.MustExec("select * from t where a > 10") - tk.MustExec("admin capture bindings") - rows = tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 1) - c.Assert(rows[0][0], Equals, "select * from `test` . `t` where `a` > ?") - c.Assert(rows[0][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` )*/ * FROM `test`.`t` WHERE `a` > 10") -} - -func (s *testSuite) TestCaptureDBCaseSensitivity(c *C) { - tk := testkit.NewTestKit(c, s.store) - s.cleanBindingEnv(tk) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("drop database if exists SPM") - tk.MustExec("create database SPM") - tk.MustExec("use SPM") - tk.MustExec("create table t(a int, b int, key(b))") - tk.MustExec("create global binding for select * from t using select /*+ use_index(t) */ * from t") - c.Assert(tk.Se.Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil), IsTrue) - tk.MustExec("select /*+ use_index(t,b) */ * from t") - tk.MustExec("select /*+ use_index(t,b) */ * from t") - tk.MustExec("admin capture bindings") - // The capture should ignore the case sensitivity for DB name when checking if any binding exists, - // so there would be no new binding captured. - rows := tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 1) - c.Assert(rows[0][1], Equals, "SELECT /*+ use_index(`t` )*/ * FROM `SPM`.`t`") - c.Assert(rows[0][8], Equals, "manual") -} - -func (s *testSuite) TestCaptureBaselinesDefaultDB(c *C) { - tk := testkit.NewTestKit(c, s.store) - s.cleanBindingEnv(tk) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec(" set @@tidb_capture_plan_baselines = on") - defer func() { - tk.MustExec(" set @@tidb_capture_plan_baselines = off") - }() - tk.MustExec("use test") - tk.MustExec("drop database if exists spm") - tk.MustExec("create database spm") - tk.MustExec("create table spm.t(a int, index idx_a(a))") - c.Assert(tk.Se.Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil), IsTrue) - tk.MustExec("select * from spm.t ignore index(idx_a) where a > 10") - tk.MustExec("select * from spm.t ignore index(idx_a) where a > 10") - tk.MustExec("admin capture bindings") - rows := tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 1) - // Default DB should be "" when all columns have explicit database name. - c.Assert(rows[0][2], Equals, "") - c.Assert(rows[0][3], Equals, "using") - tk.MustExec("use spm") - tk.MustExec("select * from spm.t where a > 10") - // Should use TableScan because of the "ignore index" binding. - c.Assert(len(tk.Se.GetSessionVars().StmtCtx.IndexNames), Equals, 0) -} - -func (s *testSuite) TestCapturePreparedStmt(c *C) { - originalVal := config.CheckTableBeforeDrop - config.CheckTableBeforeDrop = true - defer func() { - config.CheckTableBeforeDrop = originalVal - }() - - tk := testkit.NewTestKit(c, s.store) - s.cleanBindingEnv(tk) - stmtsummary.StmtSummaryByDigestMap.Clear() - c.Assert(tk.Se.Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil), IsTrue) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, c int, key idx_b(b), key idx_c(c))") - c.Assert(tk.MustUseIndex("select * from t where b = 1 and c > 1", "idx_b(b)"), IsTrue) - tk.MustExec("prepare stmt from 'select /*+ use_index(t,idx_c) */ * from t where b = ? and c > ?'") - tk.MustExec("set @p = 1") - tk.MustExec("execute stmt using @p, @p") - tk.MustExec("execute stmt using @p, @p") - - tk.MustQuery("show global bindings").Check(testkit.Rows()) - tk.MustExec("admin capture bindings") - rows := tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 1) - c.Assert(rows[0][0], Equals, "select * from `test` . `t` where `b` = ? and `c` > ?") - c.Assert(rows[0][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idx_c`)*/ * FROM `test`.`t` WHERE `b` = ? AND `c` > ?") - - c.Assert(tk.MustUseIndex("select /*+ use_index(t,idx_b) */ * from t where b = 1 and c > 1", "idx_c(c)"), IsTrue) - tk.MustExec("admin flush bindings") - tk.MustExec("admin evolve bindings") - rows = tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 1) - c.Assert(rows[0][0], Equals, "select * from `test` . `t` where `b` = ? and `c` > ?") - c.Assert(rows[0][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idx_c`)*/ * FROM `test`.`t` WHERE `b` = ? AND `c` > ?") -} - func (s *testSuite) TestDMLEvolveBaselines(c *C) { originalVal := config.CheckTableBeforeDrop config.CheckTableBeforeDrop = true @@ -992,42 +822,6 @@ func (s *testSuite) TestHintsSetID(c *C) { c.Assert(bind.ID, Equals, "") } -func (s *testSuite) TestCapturePlanBaselineIgnoreTiFlash(c *C) { - tk := testkit.NewTestKit(c, s.store) - s.cleanBindingEnv(tk) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, key(a), key(b))") - c.Assert(tk.Se.Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil), IsTrue) - tk.MustExec("select * from t") - tk.MustExec("select * from t") - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Se) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - c.Assert(exists, IsTrue) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - // Here the plan is the TiFlash plan. - rows := tk.MustQuery("explain select * from t").Rows() - c.Assert(fmt.Sprintf("%v", rows[len(rows)-1][2]), Equals, "cop[tiflash]") - - tk.MustQuery("show global bindings").Check(testkit.Rows()) - tk.MustExec("admin capture bindings") - // Don't have the TiFlash plan even we have TiFlash replica. - rows = tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 1) - c.Assert(rows[0][0], Equals, "select * from `test` . `t`") - c.Assert(rows[0][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` )*/ * FROM `test`.`t`") -} - func (s *testSuite) TestNotEvolvePlanForReadStorageHint(c *C) { originalVal := config.CheckTableBeforeDrop config.CheckTableBeforeDrop = true @@ -1174,64 +968,6 @@ func (s *testSuite) TestInvisibleIndex(c *C) { tk.MustExec("drop binding for select * from t") } -func (s *testSuite) TestBindingSource(c *C) { - originalVal := config.CheckTableBeforeDrop - config.CheckTableBeforeDrop = true - defer func() { - config.CheckTableBeforeDrop = originalVal - }() - - tk := testkit.NewTestKit(c, s.store) - s.cleanBindingEnv(tk) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, index idx_a(a))") - - // Test Source for SQL created sql - tk.MustExec("create global binding for select * from t where a > 10 using select * from t ignore index(idx_a) where a > 10") - bindHandle := s.domain.BindHandle() - sql, hash := normalizeWithDefaultDB(c, "select * from t where a > ?", "test") - bindData := bindHandle.GetBindRecord(hash, sql, "test") - c.Check(bindData, NotNil) - c.Check(bindData.OriginalSQL, Equals, "select * from `test` . `t` where `a` > ?") - c.Assert(len(bindData.Bindings), Equals, 1) - bind := bindData.Bindings[0] - c.Assert(bind.Source, Equals, bindinfo.Manual) - - // Test Source for evolved sql - tk.MustExec("set @@tidb_evolve_plan_baselines=1") - tk.MustQuery("select * from t where a > 10") - bindHandle.SaveEvolveTasksToStore() - sql, hash = normalizeWithDefaultDB(c, "select * from t where a > ?", "test") - bindData = bindHandle.GetBindRecord(hash, sql, "test") - c.Check(bindData, NotNil) - c.Check(bindData.OriginalSQL, Equals, "select * from `test` . `t` where `a` > ?") - c.Assert(len(bindData.Bindings), Equals, 2) - bind = bindData.Bindings[1] - c.Assert(bind.Source, Equals, bindinfo.Evolve) - tk.MustExec("set @@tidb_evolve_plan_baselines=0") - - // Test Source for captured sqls - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("set @@tidb_capture_plan_baselines = on") - defer func() { - tk.MustExec("set @@tidb_capture_plan_baselines = off") - }() - tk.MustExec("use test") - c.Assert(tk.Se.Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil), IsTrue) - tk.MustExec("select * from t ignore index(idx_a) where a < 10") - tk.MustExec("select * from t ignore index(idx_a) where a < 10") - tk.MustExec("admin capture bindings") - bindHandle.CaptureBaselines() - sql, hash = normalizeWithDefaultDB(c, "select * from t where a < ?", "test") - bindData = bindHandle.GetBindRecord(hash, sql, "test") - c.Check(bindData, NotNil) - c.Check(bindData.OriginalSQL, Equals, "select * from `test` . `t` where `a` < ?") - c.Assert(len(bindData.Bindings), Equals, 1) - bind = bindData.Bindings[0] - c.Assert(bind.Source, Equals, bindinfo.Capture) -} - func (s *testSuite) TestSPMHitInfo(c *C) { tk := testkit.NewTestKit(c, s.store) s.cleanBindingEnv(tk) @@ -1317,147 +1053,6 @@ func (s *testSuite) TestDMLIndexHintBind(c *C) { c.Assert(tk.MustUseIndex("delete from t where b = 1 and c > 1", "idx_c(c)"), IsTrue) } -func (s *testSuite) TestCapturedBindingCharset(c *C) { - tk := testkit.NewTestKit(c, s.store) - s.cleanBindingEnv(tk) - stmtsummary.StmtSummaryByDigestMap.Clear() - c.Assert(tk.Se.Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil), IsTrue) - tk.MustExec("use test") - tk.MustExec("create table t(name varchar(25), index idx(name))") - - tk.MustExec("set character_set_connection = 'ascii'") - tk.MustExec("update t set name = 'hello' where name <= 'abc'") - tk.MustExec("update t set name = 'hello' where name <= 'abc'") - tk.MustExec("set character_set_connection = 'utf8mb4'") - tk.MustExec("admin capture bindings") - rows := tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 1) - c.Assert(rows[0][0], Equals, "update `test` . `t` set `name` = ? where `name` <= ?") - c.Assert(rows[0][1], Equals, "UPDATE /*+ use_index(@`upd_1` `test`.`t` `idx`)*/ `test`.`t` SET `name`='hello' WHERE `name` <= 'abc'") - // Charset and Collation are empty now, they are not used currently. - c.Assert(rows[0][6], Equals, "") - c.Assert(rows[0][7], Equals, "") -} - -func (s *testSuite) TestConcurrentCapture(c *C) { - tk := testkit.NewTestKit(c, s.store) - s.cleanBindingEnv(tk) - // Simulate an existing binding generated by concurrent CREATE BINDING, which has not been synchronized to current tidb-server yet. - // Actually, it is more common to be generated by concurrent baseline capture, I use Manual just for simpler test verification. - tk.MustExec("insert into mysql.bind_info values('select * from `test` . `t`', 'select * from `test` . `t`', '', 'using', '2000-01-01 09:00:00', '2000-01-01 09:00:00', '', '','" + - bindinfo.Manual + "')") - tk.MustQuery("select original_sql, source from mysql.bind_info where source != 'builtin'").Check(testkit.Rows( - "select * from `test` . `t` manual", - )) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int)") - c.Assert(tk.Se.Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil), IsTrue) - tk.MustExec("select * from t") - tk.MustExec("select * from t") - tk.MustExec("admin capture bindings") - tk.MustQuery("select original_sql, source, status from mysql.bind_info where source != 'builtin'").Check(testkit.Rows( - "select * from `test` . `t` manual deleted", - "select * from `test` . `t` capture using", - )) -} - -func (s *testSuite) TestUpdateSubqueryCapture(c *C) { - tk := testkit.NewTestKit(c, s.store) - s.cleanBindingEnv(tk) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1(a int, b int, c int, key idx_b(b))") - tk.MustExec("create table t2(a int, b int)") - stmtsummary.StmtSummaryByDigestMap.Clear() - c.Assert(tk.Se.Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil), IsTrue) - tk.MustExec("update t1 set b = 1 where b = 2 and (a in (select a from t2 where b = 1) or c in (select a from t2 where b = 1))") - tk.MustExec("update t1 set b = 1 where b = 2 and (a in (select a from t2 where b = 1) or c in (select a from t2 where b = 1))") - tk.MustExec("admin capture bindings") - rows := tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 1) - bindSQL := "UPDATE /*+ use_index(@`upd_1` `test`.`t1` `idx_b`), use_index(@`sel_1` `test`.`t2` ), hash_join(@`upd_1` `test`.`t1`), use_index(@`sel_2` `test`.`t2` )*/ `test`.`t1` SET `b`=1 WHERE `b` = 2 AND (`a` IN (SELECT `a` FROM `test`.`t2` WHERE `b` = 1) OR `c` IN (SELECT `a` FROM `test`.`t2` WHERE `b` = 1))" - c.Assert(rows[0][1], Equals, bindSQL) - tk.MustExec(bindSQL) - c.Assert(tk.Se.GetSessionVars().StmtCtx.GetWarnings(), HasLen, 0) -} - -func (s *testSuite) TestIssue20417(c *C) { - originalVal := config.CheckTableBeforeDrop - config.CheckTableBeforeDrop = true - defer func() { - config.CheckTableBeforeDrop = originalVal - }() - - tk := testkit.NewTestKit(c, s.store) - s.cleanBindingEnv(tk) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec(`CREATE TABLE t ( - pk VARBINARY(36) NOT NULL PRIMARY KEY, - b BIGINT NOT NULL, - c BIGINT NOT NULL, - pad VARBINARY(2048), - INDEX idxb(b), - INDEX idxc(c) - )`) - - // Test for create binding - s.cleanBindingEnv(tk) - tk.MustExec("create global binding for select * from t using select /*+ use_index(t, idxb) */ * from t") - rows := tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 1) - c.Assert(rows[0][0], Equals, "select * from `test` . `t`") - c.Assert(rows[0][1], Equals, "SELECT /*+ use_index(`t` `idxb`)*/ * FROM `test`.`t`") - c.Assert(tk.MustUseIndex("select * from t", "idxb(b)"), IsTrue) - c.Assert(tk.MustUseIndex("select * from test.t", "idxb(b)"), IsTrue) - - tk.MustExec("create global binding for select * from t WHERE b=2 AND c=3924541 using select /*+ use_index(@sel_1 test.t idxb) */ * from t WHERE b=2 AND c=3924541") - c.Assert(tk.MustUseIndex("SELECT /*+ use_index(@`sel_1` `test`.`t` `idxc`)*/ * FROM `test`.`t` WHERE `b`=2 AND `c`=3924541", "idxb(b)"), IsTrue) - c.Assert(tk.MustUseIndex("SELECT /*+ use_index(@`sel_1` `test`.`t` `idxc`)*/ * FROM `t` WHERE `b`=2 AND `c`=3924541", "idxb(b)"), IsTrue) - - // Test for capture baseline - s.cleanBindingEnv(tk) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("set @@tidb_capture_plan_baselines = on") - s.domain.BindHandle().CaptureBaselines() - c.Assert(tk.Se.Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil), IsTrue) - tk.MustExec("select * from t where b=2 and c=213124") - tk.MustExec("select * from t where b=2 and c=213124") - tk.MustExec("admin capture bindings") - rows = tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 1) - c.Assert(rows[0][0], Equals, "select * from `test` . `t` where `b` = ? and `c` = ?") - c.Assert(rows[0][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idxb`)*/ * FROM `test`.`t` WHERE `b` = 2 AND `c` = 213124") - tk.MustExec("set @@tidb_capture_plan_baselines = off") - - // Test for evolve baseline - s.cleanBindingEnv(tk) - tk.MustExec("set @@tidb_evolve_plan_baselines=1") - tk.MustExec("create global binding for select * from t WHERE c=3924541 using select /*+ use_index(@sel_1 test.t idxb) */ * from t WHERE c=3924541") - rows = tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 1) - c.Assert(rows[0][0], Equals, "select * from `test` . `t` where `c` = ?") - c.Assert(rows[0][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idxb`)*/ * FROM `test`.`t` WHERE `c` = 3924541") - tk.MustExec("select /*+ use_index(t idxc)*/ * from t where c=3924541") - c.Assert(tk.Se.GetSessionVars().StmtCtx.IndexNames[0], Equals, "t:idxb") - tk.MustExec("admin flush bindings") - rows = tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 2) - c.Assert(rows[0][0], Equals, "select * from `test` . `t` where `c` = ?") - c.Assert(rows[0][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idxc`)*/ * FROM `test`.`t` WHERE `c` = 3924541") - c.Assert(rows[0][3], Equals, "pending verify") - tk.MustExec("admin evolve bindings") - rows = tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 2) - c.Assert(rows[0][0], Equals, "select * from `test` . `t` where `c` = ?") - c.Assert(rows[0][1], Equals, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idxc`)*/ * FROM `test`.`t` WHERE `c` = 3924541") - status := rows[0][3].(string) - c.Assert(status == "using" || status == "rejected", IsTrue) - tk.MustExec("set @@tidb_evolve_plan_baselines=0") -} - func (s *testSuite) TestForbidEvolvePlanBaseLinesBeforeGA(c *C) { originalVal := config.CheckTableBeforeDrop config.CheckTableBeforeDrop = false @@ -1477,24 +1072,6 @@ func (s *testSuite) TestForbidEvolvePlanBaseLinesBeforeGA(c *C) { c.Assert(err, ErrorMatches, "Cannot enable baseline evolution feature, it is not generally available now") } -func (s *testSuite) TestCaptureWithZeroSlowLogThreshold(c *C) { - tk := testkit.NewTestKit(c, s.store) - s.cleanBindingEnv(tk) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int)") - stmtsummary.StmtSummaryByDigestMap.Clear() - c.Assert(tk.Se.Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil), IsTrue) - tk.MustExec("set tidb_slow_log_threshold = 0") - tk.MustExec("select * from t") - tk.MustExec("select * from t") - tk.MustExec("set tidb_slow_log_threshold = 300") - tk.MustExec("admin capture bindings") - rows := tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 1) - c.Assert(rows[0][0], Equals, "select * from `test` . `t`") -} - func (s *testSuite) TestExplainTableStmts(c *C) { tk := testkit.NewTestKit(c, s.store) s.cleanBindingEnv(tk) @@ -1537,73 +1114,6 @@ func (s *testSuite) TestBindingWithoutCharset(c *C) { c.Assert(rows[0][1], Equals, "SELECT * FROM `test`.`t` WHERE `a` = 'aa'") } -func (s *testSuite) TestIssue25505(c *C) { - tk := testkit.NewTestKit(c, s.store) - stmtsummary.StmtSummaryByDigestMap.Clear() - s.cleanBindingEnv(tk) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - defer func() { - tk.MustExec("set tidb_slow_log_threshold = 300") - }() - tk.MustExec("set tidb_slow_log_threshold = 0") - tk.MustExec("create table t (a int(11) default null,b int(11) default null,key b (b),key ba (b))") - tk.MustExec("create table t1 (a int(11) default null,b int(11) default null,key idx_ab (a,b),key idx_a (a),key idx_b (b))") - tk.MustExec("create table t2 (a int(11) default null,b int(11) default null,key idx_ab (a,b),key idx_a (a),key idx_b (b))") - c.Assert(tk.Se.Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil), IsTrue) - - spmMap := map[string]string{} - spmMap["with recursive `cte` ( `a` ) as ( select ? union select `a` + ? from `test` . `t1` where `a` < ? ) select * from `cte`"] = - "WITH RECURSIVE `cte` (`a`) AS (SELECT 2 UNION SELECT `a` + 1 FROM `test`.`t1` WHERE `a` < 5) SELECT /*+ use_index(@`sel_3` `test`.`t1` `idx_ab`), hash_agg(@`sel_1`)*/ * FROM `cte`" - spmMap["with recursive `cte1` ( `a` , `b` ) as ( select * from `test` . `t` where `b` = ? union select `a` + ? , `b` + ? from `cte1` where `a` < ? ) select * from `test` . `t`"] = - "WITH RECURSIVE `cte1` (`a`, `b`) AS (SELECT * FROM `test`.`t` WHERE `b` = 1 UNION SELECT `a` + 1,`b` + 1 FROM `cte1` WHERE `a` < 2) SELECT /*+ use_index(@`sel_1` `test`.`t` )*/ * FROM `test`.`t`" - spmMap["with `cte1` as ( select * from `test` . `t` ) , `cte2` as ( select ? ) select * from `test` . `t`"] = - "WITH `cte1` AS (SELECT * FROM `test`.`t`), `cte2` AS (SELECT 4) SELECT /*+ use_index(@`sel_1` `test`.`t` )*/ * FROM `test`.`t`" - spmMap["with `cte` as ( select * from `test` . `t` where `b` = ? ) select * from `test` . `t`"] = - "WITH `cte` AS (SELECT * FROM `test`.`t` WHERE `b` = 6) SELECT /*+ use_index(@`sel_1` `test`.`t` )*/ * FROM `test`.`t`" - spmMap["with recursive `cte` ( `a` ) as ( select ? union select `a` + ? from `test` . `t1` where `a` > ? ) select * from `cte`"] = - "WITH RECURSIVE `cte` (`a`) AS (SELECT 2 UNION SELECT `a` + 1 FROM `test`.`t1` WHERE `a` > 5) SELECT /*+ use_index(@`sel_3` `test`.`t1` `idx_b`), hash_agg(@`sel_1`)*/ * FROM `cte`" - spmMap["with `cte` as ( with `cte1` as ( select * from `test` . `t2` where `a` > ? and `b` > ? ) select * from `cte1` ) select * from `cte` join `test` . `t1` on `t1` . `a` = `cte` . `a`"] = - "WITH `cte` AS (WITH `cte1` AS (SELECT * FROM `test`.`t2` WHERE `a` > 1 AND `b` > 1) SELECT * FROM `cte1`) SELECT /*+ use_index(@`sel_3` `test`.`t2` `idx_ab`), use_index(@`sel_1` `test`.`t1` `idx_ab`), inl_join(@`sel_1` `test`.`t1`)*/ * FROM `cte` JOIN `test`.`t1` ON `t1`.`a` = `cte`.`a`" - spmMap["with `cte` as ( with `cte1` as ( select * from `test` . `t2` where `a` = ? and `b` = ? ) select * from `cte1` ) select * from `cte` join `test` . `t1` on `t1` . `a` = `cte` . `a`"] = - "WITH `cte` AS (WITH `cte1` AS (SELECT * FROM `test`.`t2` WHERE `a` = 1 AND `b` = 1) SELECT * FROM `cte1`) SELECT /*+ use_index(@`sel_3` `test`.`t2` `idx_a`), use_index(@`sel_1` `test`.`t1` `idx_a`), inl_join(@`sel_1` `test`.`t1`)*/ * FROM `cte` JOIN `test`.`t1` ON `t1`.`a` = `cte`.`a`" - - tk.MustExec("with cte as (with cte1 as (select /*+use_index(t2 idx_a)*/ * from t2 where a = 1 and b = 1) select * from cte1) select /*+use_index(t1 idx_a)*/ * from cte join t1 on t1.a=cte.a;") - tk.MustExec("with cte as (with cte1 as (select /*+use_index(t2 idx_a)*/ * from t2 where a = 1 and b = 1) select * from cte1) select /*+use_index(t1 idx_a)*/ * from cte join t1 on t1.a=cte.a;") - tk.MustExec("with cte as (with cte1 as (select /*+use_index(t2 idx_a)*/ * from t2 where a = 1 and b = 1) select * from cte1) select /*+use_index(t1 idx_a)*/ * from cte join t1 on t1.a=cte.a;") - - tk.MustExec("with cte as (with cte1 as (select * from t2 use index(idx_ab) where a > 1 and b > 1) select * from cte1) select /*+use_index(t1 idx_ab)*/ * from cte join t1 on t1.a=cte.a;") - tk.MustExec("with cte as (with cte1 as (select * from t2 use index(idx_ab) where a > 1 and b > 1) select * from cte1) select /*+use_index(t1 idx_ab)*/ * from cte join t1 on t1.a=cte.a;") - tk.MustExec("with cte as (with cte1 as (select * from t2 use index(idx_ab) where a > 1 and b > 1) select * from cte1) select /*+use_index(t1 idx_ab)*/ * from cte join t1 on t1.a=cte.a;") - - tk.MustExec("WITH RECURSIVE cte(a) AS (SELECT 2 UNION SELECT a+1 FROM t1 use index(idx_ab) WHERE a < 5) SELECT * FROM cte;") - tk.MustExec("WITH RECURSIVE cte(a) AS (SELECT 2 UNION SELECT a+1 FROM t1 use index(idx_ab) WHERE a < 5) SELECT * FROM cte;") - tk.MustExec("WITH RECURSIVE cte(a) AS (SELECT 2 UNION SELECT a+1 FROM t1 use index(idx_ab) WHERE a < 5) SELECT * FROM cte;") - - tk.MustExec("WITH RECURSIVE cte(a) AS (SELECT 2 UNION SELECT /*+use_index(t1 idx_b)*/ a+1 FROM t1 WHERE a > 5) SELECT * FROM cte;") - tk.MustExec("WITH RECURSIVE cte(a) AS (SELECT 2 UNION SELECT /*+use_index(t1 idx_b)*/ a+1 FROM t1 WHERE a > 5) SELECT * FROM cte;") - tk.MustExec("WITH RECURSIVE cte(a) AS (SELECT 2 UNION SELECT /*+use_index(t1 idx_b)*/ a+1 FROM t1 WHERE a > 5) SELECT * FROM cte;") - - tk.MustExec("with cte as (select * from t where b=6) select * from t") - tk.MustExec("with cte as (select * from t where b=6) select * from t") - tk.MustExec("with cte as (select * from t where b=6) select * from t") - - tk.MustExec("with cte1 as (select * from t), cte2 as (select 4) select * from t") - tk.MustExec("with cte1 as (select * from t), cte2 as (select 5) select * from t") - tk.MustExec("with cte1 as (select * from t), cte2 as (select 6) select * from t") - - tk.MustExec("with recursive cte1(a,b) as (select * from t where b = 1 union select a+1,b+1 from cte1 where a < 2) select * from t") - tk.MustExec("with recursive cte1(a,b) as (select * from t where b = 1 union select a+1,b+1 from cte1 where a < 2) select * from t") - tk.MustExec("with recursive cte1(a,b) as (select * from t where b = 1 union select a+1,b+1 from cte1 where a < 2) select * from t") - tk.MustExec("admin capture bindings") - rows := tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 7) - for _, row := range rows { - str := fmt.Sprintf("%s", row[0]) - c.Assert(row[1], Equals, spmMap[str]) - } -} - func (s *testSuite) TestGCBindRecord(c *C) { tk := testkit.NewTestKit(c, s.store) s.cleanBindingEnv(tk) @@ -1719,157 +1229,3 @@ func (s *testSerialSuite) TestIssue27422(c *C) { tk.MustGetErrCode(q, errno.ErrOptOnTemporaryTable) } } - -func (s *testSuite) TestCaptureFilter(c *C) { - tk := testkit.NewTestKit(c, s.store) - s.cleanBindingEnv(tk) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec(" set @@tidb_capture_plan_baselines = on") - defer func() { - tk.MustExec(" set @@tidb_capture_plan_baselines = off") - }() - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int)") - - c.Assert(tk.Se.Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil), IsTrue) - tk.MustExec("select * from t where a > 10") - tk.MustExec("select * from t where a > 10") - tk.MustExec("admin capture bindings") - rows := tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 1) - c.Assert(rows[0][0], Equals, "select * from `test` . `t` where `a` > ?") - - // Valid table filter. - s.cleanBindingEnv(tk) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('table', 'test.t')") - tk.MustExec("select * from t where a > 10") - tk.MustExec("select * from t where a > 10") - tk.MustExec("admin capture bindings") - rows = tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 0) - tk.MustExec("select * from mysql.capture_plan_baselines_blacklist") - tk.MustExec("select * from mysql.capture_plan_baselines_blacklist") - tk.MustExec("admin capture bindings") - rows = tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 1) - c.Assert(rows[0][0], Equals, "select * from `mysql` . `capture_plan_baselines_blacklist`") - - tk.MustExec("delete from mysql.capture_plan_baselines_blacklist") - tk.MustExec("admin capture bindings") - rows = tk.MustQuery("show global bindings").Sort().Rows() - c.Assert(len(rows), Equals, 2) - c.Assert(rows[0][0], Equals, "select * from `mysql` . `capture_plan_baselines_blacklist`") - c.Assert(rows[1][0], Equals, "select * from `test` . `t` where `a` > ?") - - // Invalid table filter. - s.cleanBindingEnv(tk) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('table', 't')") - tk.MustExec("select * from t where a > 10") - tk.MustExec("select * from t where a > 10") - tk.MustExec("admin capture bindings") - rows = tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 1) - c.Assert(rows[0][0], Equals, "select * from `test` . `t` where `a` > ?") - - // Valid database filter. - s.cleanBindingEnv(tk) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('db', 'mysql')") - tk.MustExec("select * from mysql.capture_plan_baselines_blacklist") - tk.MustExec("select * from mysql.capture_plan_baselines_blacklist") - tk.MustExec("admin capture bindings") - rows = tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 0) - tk.MustExec("select * from t where a > 10") - tk.MustExec("select * from t where a > 10") - tk.MustExec("admin capture bindings") - rows = tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 1) - c.Assert(rows[0][0], Equals, "select * from `test` . `t` where `a` > ?") - - tk.MustExec("delete from mysql.capture_plan_baselines_blacklist") - tk.MustExec("admin capture bindings") - rows = tk.MustQuery("show global bindings").Sort().Rows() - c.Assert(len(rows), Equals, 2) - c.Assert(rows[0][0], Equals, "select * from `mysql` . `capture_plan_baselines_blacklist`") - c.Assert(rows[1][0], Equals, "select * from `test` . `t` where `a` > ?") - - // Valid frequency filter. - s.cleanBindingEnv(tk) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('frequency', '2')") - tk.MustExec("select * from t where a > 10") - tk.MustExec("select * from t where a > 10") - tk.MustExec("admin capture bindings") - rows = tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 0) - - tk.MustExec("select * from t where a > 10") - tk.MustExec("admin capture bindings") - rows = tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 1) - c.Assert(rows[0][0], Equals, "select * from `test` . `t` where `a` > ?") - tk.MustExec("delete from mysql.capture_plan_baselines_blacklist") - - // Invalid frequency filter. - s.cleanBindingEnv(tk) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('frequency', '0')") - tk.MustExec("select * from t where a > 10") - tk.MustExec("admin capture bindings") - rows = tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 0) - - tk.MustExec("select * from t where a > 10") - tk.MustExec("admin capture bindings") - rows = tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 1) - c.Assert(rows[0][0], Equals, "select * from `test` . `t` where `a` > ?") - tk.MustExec("delete from mysql.capture_plan_baselines_blacklist") - - // Invalid filter type. - s.cleanBindingEnv(tk) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('unknown', 'xx')") - tk.MustExec("select * from t where a > 10") - tk.MustExec("select * from t where a > 10") - tk.MustExec("admin capture bindings") - rows = tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 1) - c.Assert(rows[0][0], Equals, "select * from `test` . `t` where `a` > ?") - tk.MustExec("delete from mysql.capture_plan_baselines_blacklist") - - // Case sensitivity. - s.cleanBindingEnv(tk) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('tABle', 'tESt.T')") - tk.MustExec("select * from t where a > 10") - tk.MustExec("select * from t where a > 10") - tk.MustExec("admin capture bindings") - rows = tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 0) - - tk.MustExec("delete from mysql.capture_plan_baselines_blacklist") - tk.MustExec("admin capture bindings") - rows = tk.MustQuery("show global bindings").Sort().Rows() - c.Assert(len(rows), Equals, 1) - c.Assert(rows[0][0], Equals, "select * from `test` . `t` where `a` > ?") - - s.cleanBindingEnv(tk) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('Db', 'mySQl')") - tk.MustExec("select * from mysql.capture_plan_baselines_blacklist") - tk.MustExec("select * from mysql.capture_plan_baselines_blacklist") - tk.MustExec("admin capture bindings") - rows = tk.MustQuery("show global bindings").Rows() - c.Assert(len(rows), Equals, 0) - - tk.MustExec("delete from mysql.capture_plan_baselines_blacklist") - tk.MustExec("admin capture bindings") - rows = tk.MustQuery("show global bindings").Sort().Rows() - c.Assert(len(rows), Equals, 1) - c.Assert(rows[0][0], Equals, "select * from `mysql` . `capture_plan_baselines_blacklist`") -} diff --git a/bindinfo/capture_serial_test.go b/bindinfo/capture_serial_test.go new file mode 100644 index 0000000000000..04dbf856b51c1 --- /dev/null +++ b/bindinfo/capture_serial_test.go @@ -0,0 +1,714 @@ +// Copyright 2021 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 bindinfo_test + +import ( + "fmt" + "testing" + + "github.com/pingcap/parser/auth" + "github.com/pingcap/parser/model" + "github.com/pingcap/tidb/bindinfo" + "github.com/pingcap/tidb/config" + "github.com/pingcap/tidb/domain" + "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/util/stmtsummary" + "github.com/stretchr/testify/require" +) + +func TestDMLCapturePlanBaseline(t *testing.T) { + store, dom, clean := testkit.CreateMockStoreAndDomain(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + utilCleanBindingEnv(tk, dom) + stmtsummary.StmtSummaryByDigestMap.Clear() + tk.MustExec(" set @@tidb_capture_plan_baselines = on") + defer func() { + tk.MustExec(" set @@tidb_capture_plan_baselines = off") + }() + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, c int, key idx_b(b), key idx_c(c))") + tk.MustExec("create table t1 like t") + dom.BindHandle().CaptureBaselines() + tk.MustQuery("show global bindings").Check(testkit.Rows()) + tk.MustExec("delete from t where b = 1 and c > 1") + tk.MustExec("delete from t where b = 1 and c > 1") + tk.MustExec("update t set a = 1 where b = 1 and c > 1") + tk.MustExec("update t set a = 1 where b = 1 and c > 1") + tk.MustExec("insert into t1 select * from t where t.b = 1 and t.c > 1") + tk.MustExec("insert into t1 select * from t where t.b = 1 and t.c > 1") + tk.MustExec("replace into t1 select * from t where t.b = 1 and t.c > 1") + tk.MustExec("replace into t1 select * from t where t.b = 1 and t.c > 1") + tk.MustExec("insert into t1 values(1,1,1)") + tk.MustExec("insert into t1 values(1,1,1)") + tk.MustExec("replace into t1 values(1,1,1)") + tk.MustExec("replace into t1 values(1,1,1)") + tk.MustExec("admin capture bindings") + rows := tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 0) + + require.True(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil)) + tk.MustExec("delete from t where b = 1 and c > 1") + tk.MustExec("delete from t where b = 1 and c > 1") + tk.MustExec("update t set a = 1 where b = 1 and c > 1") + tk.MustExec("update t set a = 1 where b = 1 and c > 1") + tk.MustExec("insert into t1 select * from t where t.b = 1 and t.c > 1") + tk.MustExec("insert into t1 select * from t where t.b = 1 and t.c > 1") + tk.MustExec("replace into t1 select * from t where t.b = 1 and t.c > 1") + tk.MustExec("replace into t1 select * from t where t.b = 1 and t.c > 1") + tk.MustExec("insert into t1 values(1,1,1)") + tk.MustExec("insert into t1 values(1,1,1)") + tk.MustExec("replace into t1 values(1,1,1)") + tk.MustExec("replace into t1 values(1,1,1)") + tk.MustExec("admin capture bindings") + rows = tk.MustQuery("show global bindings").Sort().Rows() + require.Len(t, rows, 4) + require.Equal(t, "delete from `test` . `t` where `b` = ? and `c` > ?", rows[0][0]) + require.Equal(t, "DELETE /*+ use_index(@`del_1` `test`.`t` `idx_b`)*/ FROM `test`.`t` WHERE `b` = 1 AND `c` > 1", rows[0][1]) + require.Equal(t, "insert into `test` . `t1` select * from `test` . `t` where `t` . `b` = ? and `t` . `c` > ?", rows[1][0]) + require.Equal(t, "INSERT INTO `test`.`t1` SELECT /*+ use_index(@`sel_1` `test`.`t` `idx_b`)*/ * FROM `test`.`t` WHERE `t`.`b` = 1 AND `t`.`c` > 1", rows[1][1]) + require.Equal(t, "replace into `test` . `t1` select * from `test` . `t` where `t` . `b` = ? and `t` . `c` > ?", rows[2][0]) + require.Equal(t, "REPLACE INTO `test`.`t1` SELECT /*+ use_index(@`sel_1` `test`.`t` `idx_b`)*/ * FROM `test`.`t` WHERE `t`.`b` = 1 AND `t`.`c` > 1", rows[2][1]) + require.Equal(t, "update `test` . `t` set `a` = ? where `b` = ? and `c` > ?", rows[3][0]) + require.Equal(t, "UPDATE /*+ use_index(@`upd_1` `test`.`t` `idx_b`)*/ `test`.`t` SET `a`=1 WHERE `b` = 1 AND `c` > 1", rows[3][1]) +} + +func TestCapturePlanBaseline(t *testing.T) { + store, dom, clean := testkit.CreateMockStoreAndDomain(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + utilCleanBindingEnv(tk, dom) + stmtsummary.StmtSummaryByDigestMap.Clear() + tk.MustExec(" set @@tidb_capture_plan_baselines = on") + defer func() { + tk.MustExec(" set @@tidb_capture_plan_baselines = off") + }() + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int)") + dom.BindHandle().CaptureBaselines() + tk.MustQuery("show global bindings").Check(testkit.Rows()) + tk.MustExec("select count(*) from t where a > 10") + tk.MustExec("select count(*) from t where a > 10") + tk.MustExec("admin capture bindings") + rows := tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 0) + + require.True(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil)) + tk.MustExec("select * from t where a > 10") + tk.MustExec("select * from t where a > 10") + tk.MustExec("admin capture bindings") + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + require.Equal(t, "select * from `test` . `t` where `a` > ?", rows[0][0]) + require.Equal(t, "SELECT /*+ use_index(@`sel_1` `test`.`t` )*/ * FROM `test`.`t` WHERE `a` > 10", rows[0][1]) +} + +func TestCaptureDBCaseSensitivity(t *testing.T) { + store, dom, clean := testkit.CreateMockStoreAndDomain(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + utilCleanBindingEnv(tk, dom) + stmtsummary.StmtSummaryByDigestMap.Clear() + tk.MustExec("drop database if exists SPM") + tk.MustExec("create database SPM") + tk.MustExec("use SPM") + tk.MustExec("create table t(a int, b int, key(b))") + tk.MustExec("create global binding for select * from t using select /*+ use_index(t) */ * from t") + require.True(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil)) + tk.MustExec("select /*+ use_index(t,b) */ * from t") + tk.MustExec("select /*+ use_index(t,b) */ * from t") + tk.MustExec("admin capture bindings") + // The capture should ignore the case sensitivity for DB name when checking if any binding exists, + // so there would be no new binding captured. + rows := tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + require.Equal(t, "SELECT /*+ use_index(`t` )*/ * FROM `SPM`.`t`", rows[0][1]) + require.Equal(t, "manual", rows[0][8]) +} + +func TestCaptureBaselinesDefaultDB(t *testing.T) { + store, dom, clean := testkit.CreateMockStoreAndDomain(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + utilCleanBindingEnv(tk, dom) + stmtsummary.StmtSummaryByDigestMap.Clear() + tk.MustExec(" set @@tidb_capture_plan_baselines = on") + defer func() { + tk.MustExec(" set @@tidb_capture_plan_baselines = off") + }() + tk.MustExec("use test") + tk.MustExec("drop database if exists spm") + tk.MustExec("create database spm") + tk.MustExec("create table spm.t(a int, index idx_a(a))") + require.True(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil)) + tk.MustExec("select * from spm.t ignore index(idx_a) where a > 10") + tk.MustExec("select * from spm.t ignore index(idx_a) where a > 10") + tk.MustExec("admin capture bindings") + rows := tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + // Default DB should be "" when all columns have explicit database name. + require.Equal(t, "", rows[0][2]) + require.Equal(t, "using", rows[0][3]) + tk.MustExec("use spm") + tk.MustExec("select * from spm.t where a > 10") + // Should use TableScan because of the "ignore index" binding. + require.Len(t, tk.Session().GetSessionVars().StmtCtx.IndexNames, 0) +} + +func TestCapturePreparedStmt(t *testing.T) { + originalVal := config.CheckTableBeforeDrop + config.CheckTableBeforeDrop = true + defer func() { + config.CheckTableBeforeDrop = originalVal + }() + + store, dom, clean := testkit.CreateMockStoreAndDomain(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + utilCleanBindingEnv(tk, dom) + stmtsummary.StmtSummaryByDigestMap.Clear() + require.True(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil)) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, c int, key idx_b(b), key idx_c(c))") + require.True(t, tk.MustUseIndex("select * from t where b = 1 and c > 1", "idx_b(b)")) + tk.MustExec("prepare stmt from 'select /*+ use_index(t,idx_c) */ * from t where b = ? and c > ?'") + tk.MustExec("set @p = 1") + tk.MustExec("execute stmt using @p, @p") + tk.MustExec("execute stmt using @p, @p") + + tk.MustQuery("show global bindings").Check(testkit.Rows()) + tk.MustExec("admin capture bindings") + rows := tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + require.Equal(t, "select * from `test` . `t` where `b` = ? and `c` > ?", rows[0][0]) + require.Equal(t, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idx_c`)*/ * FROM `test`.`t` WHERE `b` = ? AND `c` > ?", rows[0][1]) + + require.True(t, tk.MustUseIndex("select /*+ use_index(t,idx_b) */ * from t where b = 1 and c > 1", "idx_c(c)")) + tk.MustExec("admin flush bindings") + tk.MustExec("admin evolve bindings") + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + require.Equal(t, "select * from `test` . `t` where `b` = ? and `c` > ?", rows[0][0]) + require.Equal(t, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idx_c`)*/ * FROM `test`.`t` WHERE `b` = ? AND `c` > ?", rows[0][1]) +} + +func TestCapturePlanBaselineIgnoreTiFlash(t *testing.T) { + store, dom, clean := testkit.CreateMockStoreAndDomain(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + utilCleanBindingEnv(tk, dom) + stmtsummary.StmtSummaryByDigestMap.Clear() + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, key(a), key(b))") + require.True(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil)) + tk.MustExec("select * from t") + tk.MustExec("select * from t") + // Create virtual tiflash replica info. + domSession := domain.GetDomain(tk.Session()) + is := domSession.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + // Here the plan is the TiFlash plan. + rows := tk.MustQuery("explain select * from t").Rows() + require.Equal(t, "cop[tiflash]", fmt.Sprintf("%v", rows[len(rows)-1][2])) + + tk.MustQuery("show global bindings").Check(testkit.Rows()) + tk.MustExec("admin capture bindings") + // Don't have the TiFlash plan even we have TiFlash replica. + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + require.Equal(t, "select * from `test` . `t`", rows[0][0]) + require.Equal(t, "SELECT /*+ use_index(@`sel_1` `test`.`t` )*/ * FROM `test`.`t`", rows[0][1]) +} + +func TestBindingSource(t *testing.T) { + originalVal := config.CheckTableBeforeDrop + config.CheckTableBeforeDrop = true + defer func() { + config.CheckTableBeforeDrop = originalVal + }() + + store, dom, clean := testkit.CreateMockStoreAndDomain(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + utilCleanBindingEnv(tk, dom) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, index idx_a(a))") + + // Test Source for SQL created sql + tk.MustExec("create global binding for select * from t where a > 10 using select * from t ignore index(idx_a) where a > 10") + bindHandle := dom.BindHandle() + sql, hash := utilNormalizeWithDefaultDB(t, "select * from t where a > ?", "test") + bindData := bindHandle.GetBindRecord(hash, sql, "test") + require.NotNil(t, bindData) + require.Equal(t, "select * from `test` . `t` where `a` > ?", bindData.OriginalSQL) + require.Len(t, bindData.Bindings, 1) + bind := bindData.Bindings[0] + require.Equal(t, bindinfo.Manual, bind.Source) + + // Test Source for evolved sql + tk.MustExec("set @@tidb_evolve_plan_baselines=1") + tk.MustQuery("select * from t where a > 10") + bindHandle.SaveEvolveTasksToStore() + sql, hash = utilNormalizeWithDefaultDB(t, "select * from t where a > ?", "test") + bindData = bindHandle.GetBindRecord(hash, sql, "test") + require.NotNil(t, bindData) + require.Equal(t, "select * from `test` . `t` where `a` > ?", bindData.OriginalSQL) + require.Len(t, bindData.Bindings, 2) + bind = bindData.Bindings[1] + require.Equal(t, bindinfo.Evolve, bind.Source) + tk.MustExec("set @@tidb_evolve_plan_baselines=0") + + // Test Source for captured sqls + stmtsummary.StmtSummaryByDigestMap.Clear() + tk.MustExec("set @@tidb_capture_plan_baselines = on") + defer func() { + tk.MustExec("set @@tidb_capture_plan_baselines = off") + }() + tk.MustExec("use test") + require.True(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil)) + tk.MustExec("select * from t ignore index(idx_a) where a < 10") + tk.MustExec("select * from t ignore index(idx_a) where a < 10") + tk.MustExec("admin capture bindings") + bindHandle.CaptureBaselines() + sql, hash = utilNormalizeWithDefaultDB(t, "select * from t where a < ?", "test") + bindData = bindHandle.GetBindRecord(hash, sql, "test") + require.NotNil(t, bindData) + require.Equal(t, "select * from `test` . `t` where `a` < ?", bindData.OriginalSQL) + require.Len(t, bindData.Bindings, 1) + bind = bindData.Bindings[0] + require.Equal(t, bindinfo.Capture, bind.Source) +} + +func TestCapturedBindingCharset(t *testing.T) { + store, dom, clean := testkit.CreateMockStoreAndDomain(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + utilCleanBindingEnv(tk, dom) + stmtsummary.StmtSummaryByDigestMap.Clear() + require.True(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil)) + tk.MustExec("use test") + tk.MustExec("create table t(name varchar(25), index idx(name))") + + tk.MustExec("set character_set_connection = 'ascii'") + tk.MustExec("update t set name = 'hello' where name <= 'abc'") + tk.MustExec("update t set name = 'hello' where name <= 'abc'") + tk.MustExec("set character_set_connection = 'utf8mb4'") + tk.MustExec("admin capture bindings") + rows := tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + require.Equal(t, "update `test` . `t` set `name` = ? where `name` <= ?", rows[0][0]) + require.Equal(t, "UPDATE /*+ use_index(@`upd_1` `test`.`t` `idx`)*/ `test`.`t` SET `name`='hello' WHERE `name` <= 'abc'", rows[0][1]) + // Charset and Collation are empty now, they are not used currently. + require.Equal(t, "", rows[0][6]) + require.Equal(t, "", rows[0][7]) +} + +func TestConcurrentCapture(t *testing.T) { + store, dom, clean := testkit.CreateMockStoreAndDomain(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + utilCleanBindingEnv(tk, dom) + // Simulate an existing binding generated by concurrent CREATE BINDING, which has not been synchronized to current tidb-server yet. + // Actually, it is more common to be generated by concurrent baseline capture, I use Manual just for simpler test verification. + tk.MustExec("insert into mysql.bind_info values('select * from `test` . `t`', 'select * from `test` . `t`', '', 'using', '2000-01-01 09:00:00', '2000-01-01 09:00:00', '', '','" + + bindinfo.Manual + "')") + tk.MustQuery("select original_sql, source from mysql.bind_info where source != 'builtin'").Check(testkit.Rows( + "select * from `test` . `t` manual", + )) + stmtsummary.StmtSummaryByDigestMap.Clear() + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int)") + require.True(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil)) + tk.MustExec("select * from t") + tk.MustExec("select * from t") + tk.MustExec("admin capture bindings") + tk.MustQuery("select original_sql, source, status from mysql.bind_info where source != 'builtin'").Check(testkit.Rows( + "select * from `test` . `t` manual deleted", + "select * from `test` . `t` capture using", + )) +} + +func TestUpdateSubqueryCapture(t *testing.T) { + store, dom, clean := testkit.CreateMockStoreAndDomain(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + utilCleanBindingEnv(tk, dom) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1(a int, b int, c int, key idx_b(b))") + tk.MustExec("create table t2(a int, b int)") + stmtsummary.StmtSummaryByDigestMap.Clear() + require.True(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil)) + tk.MustExec("update t1 set b = 1 where b = 2 and (a in (select a from t2 where b = 1) or c in (select a from t2 where b = 1))") + tk.MustExec("update t1 set b = 1 where b = 2 and (a in (select a from t2 where b = 1) or c in (select a from t2 where b = 1))") + tk.MustExec("admin capture bindings") + rows := tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + bindSQL := "UPDATE /*+ use_index(@`upd_1` `test`.`t1` `idx_b`), use_index(@`sel_1` `test`.`t2` ), hash_join(@`upd_1` `test`.`t1`), use_index(@`sel_2` `test`.`t2` )*/ `test`.`t1` SET `b`=1 WHERE `b` = 2 AND (`a` IN (SELECT `a` FROM `test`.`t2` WHERE `b` = 1) OR `c` IN (SELECT `a` FROM `test`.`t2` WHERE `b` = 1))" + require.Equal(t, bindSQL, rows[0][1]) + tk.MustExec(bindSQL) + require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 0) +} + +func TestIssue20417(t *testing.T) { + originalVal := config.CheckTableBeforeDrop + config.CheckTableBeforeDrop = true + defer func() { + config.CheckTableBeforeDrop = originalVal + }() + + store, dom, clean := testkit.CreateMockStoreAndDomain(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + utilCleanBindingEnv(tk, dom) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec(`CREATE TABLE t ( + pk VARBINARY(36) NOT NULL PRIMARY KEY, + b BIGINT NOT NULL, + c BIGINT NOT NULL, + pad VARBINARY(2048), + INDEX idxb(b), + INDEX idxc(c) + )`) + + // Test for create binding + utilCleanBindingEnv(tk, dom) + tk.MustExec("create global binding for select * from t using select /*+ use_index(t, idxb) */ * from t") + rows := tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + require.Equal(t, "select * from `test` . `t`", rows[0][0]) + require.Equal(t, "SELECT /*+ use_index(`t` `idxb`)*/ * FROM `test`.`t`", rows[0][1]) + require.True(t, tk.MustUseIndex("select * from t", "idxb(b)")) + require.True(t, tk.MustUseIndex("select * from test.t", "idxb(b)")) + + tk.MustExec("create global binding for select * from t WHERE b=2 AND c=3924541 using select /*+ use_index(@sel_1 test.t idxb) */ * from t WHERE b=2 AND c=3924541") + require.True(t, tk.MustUseIndex("SELECT /*+ use_index(@`sel_1` `test`.`t` `idxc`)*/ * FROM `test`.`t` WHERE `b`=2 AND `c`=3924541", "idxb(b)")) + require.True(t, tk.MustUseIndex("SELECT /*+ use_index(@`sel_1` `test`.`t` `idxc`)*/ * FROM `t` WHERE `b`=2 AND `c`=3924541", "idxb(b)")) + + // Test for capture baseline + utilCleanBindingEnv(tk, dom) + stmtsummary.StmtSummaryByDigestMap.Clear() + tk.MustExec("set @@tidb_capture_plan_baselines = on") + dom.BindHandle().CaptureBaselines() + require.True(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil)) + tk.MustExec("select * from t where b=2 and c=213124") + tk.MustExec("select * from t where b=2 and c=213124") + tk.MustExec("admin capture bindings") + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + require.Equal(t, "select * from `test` . `t` where `b` = ? and `c` = ?", rows[0][0]) + require.Equal(t, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idxb`)*/ * FROM `test`.`t` WHERE `b` = 2 AND `c` = 213124", rows[0][1]) + tk.MustExec("set @@tidb_capture_plan_baselines = off") + + // Test for evolve baseline + utilCleanBindingEnv(tk, dom) + tk.MustExec("set @@tidb_evolve_plan_baselines=1") + tk.MustExec("create global binding for select * from t WHERE c=3924541 using select /*+ use_index(@sel_1 test.t idxb) */ * from t WHERE c=3924541") + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + require.Equal(t, "select * from `test` . `t` where `c` = ?", rows[0][0]) + require.Equal(t, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idxb`)*/ * FROM `test`.`t` WHERE `c` = 3924541", rows[0][1]) + tk.MustExec("select /*+ use_index(t idxc)*/ * from t where c=3924541") + require.Equal(t, "t:idxb", tk.Session().GetSessionVars().StmtCtx.IndexNames[0]) + tk.MustExec("admin flush bindings") + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 2) + require.Equal(t, "select * from `test` . `t` where `c` = ?", rows[0][0]) + require.Equal(t, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idxc`)*/ * FROM `test`.`t` WHERE `c` = 3924541", rows[0][1]) + require.Equal(t, "pending verify", rows[0][3]) + tk.MustExec("admin evolve bindings") + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 2) + require.Equal(t, "select * from `test` . `t` where `c` = ?", rows[0][0]) + require.Equal(t, "SELECT /*+ use_index(@`sel_1` `test`.`t` `idxc`)*/ * FROM `test`.`t` WHERE `c` = 3924541", rows[0][1]) + status := rows[0][3].(string) + require.True(t, status == "using" || status == "rejected") + tk.MustExec("set @@tidb_evolve_plan_baselines=0") +} + +func TestCaptureWithZeroSlowLogThreshold(t *testing.T) { + store, dom, clean := testkit.CreateMockStoreAndDomain(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + utilCleanBindingEnv(tk, dom) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int)") + stmtsummary.StmtSummaryByDigestMap.Clear() + require.True(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil)) + tk.MustExec("set tidb_slow_log_threshold = 0") + tk.MustExec("select * from t") + tk.MustExec("select * from t") + tk.MustExec("set tidb_slow_log_threshold = 300") + tk.MustExec("admin capture bindings") + rows := tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + require.Equal(t, "select * from `test` . `t`", rows[0][0]) +} + +func TestIssue25505(t *testing.T) { + store, dom, clean := testkit.CreateMockStoreAndDomain(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + stmtsummary.StmtSummaryByDigestMap.Clear() + utilCleanBindingEnv(tk, dom) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + defer func() { + tk.MustExec("set tidb_slow_log_threshold = 300") + }() + tk.MustExec("set tidb_slow_log_threshold = 0") + tk.MustExec("create table t (a int(11) default null,b int(11) default null,key b (b),key ba (b))") + tk.MustExec("create table t1 (a int(11) default null,b int(11) default null,key idx_ab (a,b),key idx_a (a),key idx_b (b))") + tk.MustExec("create table t2 (a int(11) default null,b int(11) default null,key idx_ab (a,b),key idx_a (a),key idx_b (b))") + require.True(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil)) + + spmMap := map[string]string{} + spmMap["with recursive `cte` ( `a` ) as ( select ? union select `a` + ? from `test` . `t1` where `a` < ? ) select * from `cte`"] = + "WITH RECURSIVE `cte` (`a`) AS (SELECT 2 UNION SELECT `a` + 1 FROM `test`.`t1` WHERE `a` < 5) SELECT /*+ use_index(@`sel_3` `test`.`t1` `idx_ab`), hash_agg(@`sel_1`)*/ * FROM `cte`" + spmMap["with recursive `cte1` ( `a` , `b` ) as ( select * from `test` . `t` where `b` = ? union select `a` + ? , `b` + ? from `cte1` where `a` < ? ) select * from `test` . `t`"] = + "WITH RECURSIVE `cte1` (`a`, `b`) AS (SELECT * FROM `test`.`t` WHERE `b` = 1 UNION SELECT `a` + 1,`b` + 1 FROM `cte1` WHERE `a` < 2) SELECT /*+ use_index(@`sel_1` `test`.`t` )*/ * FROM `test`.`t`" + spmMap["with `cte1` as ( select * from `test` . `t` ) , `cte2` as ( select ? ) select * from `test` . `t`"] = + "WITH `cte1` AS (SELECT * FROM `test`.`t`), `cte2` AS (SELECT 4) SELECT /*+ use_index(@`sel_1` `test`.`t` )*/ * FROM `test`.`t`" + spmMap["with `cte` as ( select * from `test` . `t` where `b` = ? ) select * from `test` . `t`"] = + "WITH `cte` AS (SELECT * FROM `test`.`t` WHERE `b` = 6) SELECT /*+ use_index(@`sel_1` `test`.`t` )*/ * FROM `test`.`t`" + spmMap["with recursive `cte` ( `a` ) as ( select ? union select `a` + ? from `test` . `t1` where `a` > ? ) select * from `cte`"] = + "WITH RECURSIVE `cte` (`a`) AS (SELECT 2 UNION SELECT `a` + 1 FROM `test`.`t1` WHERE `a` > 5) SELECT /*+ use_index(@`sel_3` `test`.`t1` `idx_b`), hash_agg(@`sel_1`)*/ * FROM `cte`" + spmMap["with `cte` as ( with `cte1` as ( select * from `test` . `t2` where `a` > ? and `b` > ? ) select * from `cte1` ) select * from `cte` join `test` . `t1` on `t1` . `a` = `cte` . `a`"] = + "WITH `cte` AS (WITH `cte1` AS (SELECT * FROM `test`.`t2` WHERE `a` > 1 AND `b` > 1) SELECT * FROM `cte1`) SELECT /*+ use_index(@`sel_3` `test`.`t2` `idx_ab`), use_index(@`sel_1` `test`.`t1` `idx_ab`), inl_join(@`sel_1` `test`.`t1`)*/ * FROM `cte` JOIN `test`.`t1` ON `t1`.`a` = `cte`.`a`" + spmMap["with `cte` as ( with `cte1` as ( select * from `test` . `t2` where `a` = ? and `b` = ? ) select * from `cte1` ) select * from `cte` join `test` . `t1` on `t1` . `a` = `cte` . `a`"] = + "WITH `cte` AS (WITH `cte1` AS (SELECT * FROM `test`.`t2` WHERE `a` = 1 AND `b` = 1) SELECT * FROM `cte1`) SELECT /*+ use_index(@`sel_3` `test`.`t2` `idx_a`), use_index(@`sel_1` `test`.`t1` `idx_a`), inl_join(@`sel_1` `test`.`t1`)*/ * FROM `cte` JOIN `test`.`t1` ON `t1`.`a` = `cte`.`a`" + + tk.MustExec("with cte as (with cte1 as (select /*+use_index(t2 idx_a)*/ * from t2 where a = 1 and b = 1) select * from cte1) select /*+use_index(t1 idx_a)*/ * from cte join t1 on t1.a=cte.a;") + tk.MustExec("with cte as (with cte1 as (select /*+use_index(t2 idx_a)*/ * from t2 where a = 1 and b = 1) select * from cte1) select /*+use_index(t1 idx_a)*/ * from cte join t1 on t1.a=cte.a;") + tk.MustExec("with cte as (with cte1 as (select /*+use_index(t2 idx_a)*/ * from t2 where a = 1 and b = 1) select * from cte1) select /*+use_index(t1 idx_a)*/ * from cte join t1 on t1.a=cte.a;") + + tk.MustExec("with cte as (with cte1 as (select * from t2 use index(idx_ab) where a > 1 and b > 1) select * from cte1) select /*+use_index(t1 idx_ab)*/ * from cte join t1 on t1.a=cte.a;") + tk.MustExec("with cte as (with cte1 as (select * from t2 use index(idx_ab) where a > 1 and b > 1) select * from cte1) select /*+use_index(t1 idx_ab)*/ * from cte join t1 on t1.a=cte.a;") + tk.MustExec("with cte as (with cte1 as (select * from t2 use index(idx_ab) where a > 1 and b > 1) select * from cte1) select /*+use_index(t1 idx_ab)*/ * from cte join t1 on t1.a=cte.a;") + + tk.MustExec("WITH RECURSIVE cte(a) AS (SELECT 2 UNION SELECT a+1 FROM t1 use index(idx_ab) WHERE a < 5) SELECT * FROM cte;") + tk.MustExec("WITH RECURSIVE cte(a) AS (SELECT 2 UNION SELECT a+1 FROM t1 use index(idx_ab) WHERE a < 5) SELECT * FROM cte;") + tk.MustExec("WITH RECURSIVE cte(a) AS (SELECT 2 UNION SELECT a+1 FROM t1 use index(idx_ab) WHERE a < 5) SELECT * FROM cte;") + + tk.MustExec("WITH RECURSIVE cte(a) AS (SELECT 2 UNION SELECT /*+use_index(t1 idx_b)*/ a+1 FROM t1 WHERE a > 5) SELECT * FROM cte;") + tk.MustExec("WITH RECURSIVE cte(a) AS (SELECT 2 UNION SELECT /*+use_index(t1 idx_b)*/ a+1 FROM t1 WHERE a > 5) SELECT * FROM cte;") + tk.MustExec("WITH RECURSIVE cte(a) AS (SELECT 2 UNION SELECT /*+use_index(t1 idx_b)*/ a+1 FROM t1 WHERE a > 5) SELECT * FROM cte;") + + tk.MustExec("with cte as (select * from t where b=6) select * from t") + tk.MustExec("with cte as (select * from t where b=6) select * from t") + tk.MustExec("with cte as (select * from t where b=6) select * from t") + + tk.MustExec("with cte1 as (select * from t), cte2 as (select 4) select * from t") + tk.MustExec("with cte1 as (select * from t), cte2 as (select 5) select * from t") + tk.MustExec("with cte1 as (select * from t), cte2 as (select 6) select * from t") + + tk.MustExec("with recursive cte1(a,b) as (select * from t where b = 1 union select a+1,b+1 from cte1 where a < 2) select * from t") + tk.MustExec("with recursive cte1(a,b) as (select * from t where b = 1 union select a+1,b+1 from cte1 where a < 2) select * from t") + tk.MustExec("with recursive cte1(a,b) as (select * from t where b = 1 union select a+1,b+1 from cte1 where a < 2) select * from t") + tk.MustExec("admin capture bindings") + rows := tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 7) + for _, row := range rows { + str := fmt.Sprintf("%s", row[0]) + require.Equal(t, spmMap[str], row[1]) + } +} + +func TestCaptureFilter(t *testing.T) { + store, dom, clean := testkit.CreateMockStoreAndDomain(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + utilCleanBindingEnv(tk, dom) + stmtsummary.StmtSummaryByDigestMap.Clear() + tk.MustExec(" set @@tidb_capture_plan_baselines = on") + defer func() { + tk.MustExec(" set @@tidb_capture_plan_baselines = off") + }() + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int)") + + require.True(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil)) + tk.MustExec("select * from t where a > 10") + tk.MustExec("select * from t where a > 10") + tk.MustExec("admin capture bindings") + rows := tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + require.Equal(t, "select * from `test` . `t` where `a` > ?", rows[0][0]) + + // Valid table filter. + utilCleanBindingEnv(tk, dom) + stmtsummary.StmtSummaryByDigestMap.Clear() + tk.MustExec("insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('table', 'test.t')") + tk.MustExec("select * from t where a > 10") + tk.MustExec("select * from t where a > 10") + tk.MustExec("admin capture bindings") + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 0) + tk.MustExec("select * from mysql.capture_plan_baselines_blacklist") + tk.MustExec("select * from mysql.capture_plan_baselines_blacklist") + tk.MustExec("admin capture bindings") + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + require.Equal(t, "select * from `mysql` . `capture_plan_baselines_blacklist`", rows[0][0]) + + tk.MustExec("delete from mysql.capture_plan_baselines_blacklist") + tk.MustExec("admin capture bindings") + rows = tk.MustQuery("show global bindings").Sort().Rows() + require.Len(t, rows, 2) + require.Equal(t, "select * from `mysql` . `capture_plan_baselines_blacklist`", rows[0][0]) + require.Equal(t, "select * from `test` . `t` where `a` > ?", rows[1][0]) + + // Invalid table filter. + utilCleanBindingEnv(tk, dom) + stmtsummary.StmtSummaryByDigestMap.Clear() + tk.MustExec("insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('table', 't')") + tk.MustExec("select * from t where a > 10") + tk.MustExec("select * from t where a > 10") + tk.MustExec("admin capture bindings") + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + require.Equal(t, "select * from `test` . `t` where `a` > ?", rows[0][0]) + + // Valid database filter. + utilCleanBindingEnv(tk, dom) + stmtsummary.StmtSummaryByDigestMap.Clear() + tk.MustExec("insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('db', 'mysql')") + tk.MustExec("select * from mysql.capture_plan_baselines_blacklist") + tk.MustExec("select * from mysql.capture_plan_baselines_blacklist") + tk.MustExec("admin capture bindings") + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 0) + tk.MustExec("select * from t where a > 10") + tk.MustExec("select * from t where a > 10") + tk.MustExec("admin capture bindings") + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + require.Equal(t, "select * from `test` . `t` where `a` > ?", rows[0][0]) + + tk.MustExec("delete from mysql.capture_plan_baselines_blacklist") + tk.MustExec("admin capture bindings") + rows = tk.MustQuery("show global bindings").Sort().Rows() + require.Len(t, rows, 2) + require.Equal(t, "select * from `mysql` . `capture_plan_baselines_blacklist`", rows[0][0]) + require.Equal(t, "select * from `test` . `t` where `a` > ?", rows[1][0]) + + // Valid frequency filter. + utilCleanBindingEnv(tk, dom) + stmtsummary.StmtSummaryByDigestMap.Clear() + tk.MustExec("insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('frequency', '2')") + tk.MustExec("select * from t where a > 10") + tk.MustExec("select * from t where a > 10") + tk.MustExec("admin capture bindings") + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 0) + + tk.MustExec("select * from t where a > 10") + tk.MustExec("admin capture bindings") + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + require.Equal(t, "select * from `test` . `t` where `a` > ?", rows[0][0]) + tk.MustExec("delete from mysql.capture_plan_baselines_blacklist") + + // Invalid frequency filter. + utilCleanBindingEnv(tk, dom) + stmtsummary.StmtSummaryByDigestMap.Clear() + tk.MustExec("insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('frequency', '0')") + tk.MustExec("select * from t where a > 10") + tk.MustExec("admin capture bindings") + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 0) + + tk.MustExec("select * from t where a > 10") + tk.MustExec("admin capture bindings") + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + require.Equal(t, "select * from `test` . `t` where `a` > ?", rows[0][0]) + tk.MustExec("delete from mysql.capture_plan_baselines_blacklist") + + // Invalid filter type. + utilCleanBindingEnv(tk, dom) + stmtsummary.StmtSummaryByDigestMap.Clear() + tk.MustExec("insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('unknown', 'xx')") + tk.MustExec("select * from t where a > 10") + tk.MustExec("select * from t where a > 10") + tk.MustExec("admin capture bindings") + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + require.Equal(t, "select * from `test` . `t` where `a` > ?", rows[0][0]) + tk.MustExec("delete from mysql.capture_plan_baselines_blacklist") + + // Case sensitivity. + utilCleanBindingEnv(tk, dom) + stmtsummary.StmtSummaryByDigestMap.Clear() + tk.MustExec("insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('tABle', 'tESt.T')") + tk.MustExec("select * from t where a > 10") + tk.MustExec("select * from t where a > 10") + tk.MustExec("admin capture bindings") + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 0) + + tk.MustExec("delete from mysql.capture_plan_baselines_blacklist") + tk.MustExec("admin capture bindings") + rows = tk.MustQuery("show global bindings").Sort().Rows() + require.Len(t, rows, 1) + require.Equal(t, "select * from `test` . `t` where `a` > ?", rows[0][0]) + + utilCleanBindingEnv(tk, dom) + stmtsummary.StmtSummaryByDigestMap.Clear() + tk.MustExec("insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('Db', 'mySQl')") + tk.MustExec("select * from mysql.capture_plan_baselines_blacklist") + tk.MustExec("select * from mysql.capture_plan_baselines_blacklist") + tk.MustExec("admin capture bindings") + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 0) + + tk.MustExec("delete from mysql.capture_plan_baselines_blacklist") + tk.MustExec("admin capture bindings") + rows = tk.MustQuery("show global bindings").Sort().Rows() + require.Len(t, rows, 1) + require.Equal(t, "select * from `mysql` . `capture_plan_baselines_blacklist`", rows[0][0]) +} diff --git a/testkit/testkit.go b/testkit/testkit.go index 273b10f25294a..1afa7d6035932 100644 --- a/testkit/testkit.go +++ b/testkit/testkit.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build !codes // +build !codes package testkit @@ -195,3 +196,14 @@ func (tk *TestKit) MustGetErrMsg(sql string, errStr string) { tk.require.Error(err) tk.require.Equal(errStr, err.Error()) } + +// MustUseIndex checks if the result execution plan contains specific index(es). +func (tk *TestKit) MustUseIndex(sql string, index string, args ...interface{}) bool { + rs := tk.MustQuery("explain "+sql, args...) + for i := range rs.rows { + if strings.Contains(rs.rows[i][3], "index:"+index) { + return true + } + } + return false +} From 428cf21ef1c86dbc57233a7c29aaaed2c257c52e Mon Sep 17 00:00:00 2001 From: senhtry Date: Wed, 22 Sep 2021 16:40:43 +0800 Subject: [PATCH 2/3] fix: remove unused code --- bindinfo/capture_serial_test.go | 46 ++++++++++++++++----------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/bindinfo/capture_serial_test.go b/bindinfo/capture_serial_test.go index 04dbf856b51c1..9cb5fe3a2fc83 100644 --- a/bindinfo/capture_serial_test.go +++ b/bindinfo/capture_serial_test.go @@ -33,7 +33,7 @@ func TestDMLCapturePlanBaseline(t *testing.T) { defer clean() tk := testkit.NewTestKit(t, store) - utilCleanBindingEnv(tk, dom) + stmtsummary.StmtSummaryByDigestMap.Clear() tk.MustExec(" set @@tidb_capture_plan_baselines = on") defer func() { @@ -92,7 +92,7 @@ func TestCapturePlanBaseline(t *testing.T) { defer clean() tk := testkit.NewTestKit(t, store) - utilCleanBindingEnv(tk, dom) + stmtsummary.StmtSummaryByDigestMap.Clear() tk.MustExec(" set @@tidb_capture_plan_baselines = on") defer func() { @@ -120,11 +120,11 @@ func TestCapturePlanBaseline(t *testing.T) { } func TestCaptureDBCaseSensitivity(t *testing.T) { - store, dom, clean := testkit.CreateMockStoreAndDomain(t) + store, clean := testkit.CreateMockStore(t) defer clean() tk := testkit.NewTestKit(t, store) - utilCleanBindingEnv(tk, dom) + stmtsummary.StmtSummaryByDigestMap.Clear() tk.MustExec("drop database if exists SPM") tk.MustExec("create database SPM") @@ -144,11 +144,11 @@ func TestCaptureDBCaseSensitivity(t *testing.T) { } func TestCaptureBaselinesDefaultDB(t *testing.T) { - store, dom, clean := testkit.CreateMockStoreAndDomain(t) + store, clean := testkit.CreateMockStore(t) defer clean() tk := testkit.NewTestKit(t, store) - utilCleanBindingEnv(tk, dom) + stmtsummary.StmtSummaryByDigestMap.Clear() tk.MustExec(" set @@tidb_capture_plan_baselines = on") defer func() { @@ -180,11 +180,11 @@ func TestCapturePreparedStmt(t *testing.T) { config.CheckTableBeforeDrop = originalVal }() - store, dom, clean := testkit.CreateMockStoreAndDomain(t) + store, clean := testkit.CreateMockStore(t) defer clean() tk := testkit.NewTestKit(t, store) - utilCleanBindingEnv(tk, dom) + stmtsummary.StmtSummaryByDigestMap.Clear() require.True(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil)) tk.MustExec("use test") @@ -213,11 +213,11 @@ func TestCapturePreparedStmt(t *testing.T) { } func TestCapturePlanBaselineIgnoreTiFlash(t *testing.T) { - store, dom, clean := testkit.CreateMockStoreAndDomain(t) + store, clean := testkit.CreateMockStore(t) defer clean() tk := testkit.NewTestKit(t, store) - utilCleanBindingEnv(tk, dom) + stmtsummary.StmtSummaryByDigestMap.Clear() tk.MustExec("use test") tk.MustExec("drop table if exists t") @@ -262,7 +262,7 @@ func TestBindingSource(t *testing.T) { defer clean() tk := testkit.NewTestKit(t, store) - utilCleanBindingEnv(tk, dom) + tk.MustExec("use test") tk.MustExec("drop table if exists t") tk.MustExec("create table t(a int, index idx_a(a))") @@ -313,11 +313,11 @@ func TestBindingSource(t *testing.T) { } func TestCapturedBindingCharset(t *testing.T) { - store, dom, clean := testkit.CreateMockStoreAndDomain(t) + store, clean := testkit.CreateMockStore(t) defer clean() tk := testkit.NewTestKit(t, store) - utilCleanBindingEnv(tk, dom) + stmtsummary.StmtSummaryByDigestMap.Clear() require.True(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil)) tk.MustExec("use test") @@ -338,11 +338,11 @@ func TestCapturedBindingCharset(t *testing.T) { } func TestConcurrentCapture(t *testing.T) { - store, dom, clean := testkit.CreateMockStoreAndDomain(t) + store, clean := testkit.CreateMockStore(t) defer clean() tk := testkit.NewTestKit(t, store) - utilCleanBindingEnv(tk, dom) + // Simulate an existing binding generated by concurrent CREATE BINDING, which has not been synchronized to current tidb-server yet. // Actually, it is more common to be generated by concurrent baseline capture, I use Manual just for simpler test verification. tk.MustExec("insert into mysql.bind_info values('select * from `test` . `t`', 'select * from `test` . `t`', '', 'using', '2000-01-01 09:00:00', '2000-01-01 09:00:00', '', '','" + @@ -365,11 +365,11 @@ func TestConcurrentCapture(t *testing.T) { } func TestUpdateSubqueryCapture(t *testing.T) { - store, dom, clean := testkit.CreateMockStoreAndDomain(t) + store, clean := testkit.CreateMockStore(t) defer clean() tk := testkit.NewTestKit(t, store) - utilCleanBindingEnv(tk, dom) + tk.MustExec("use test") tk.MustExec("drop table if exists t1, t2") tk.MustExec("create table t1(a int, b int, c int, key idx_b(b))") @@ -398,7 +398,7 @@ func TestIssue20417(t *testing.T) { defer clean() tk := testkit.NewTestKit(t, store) - utilCleanBindingEnv(tk, dom) + tk.MustExec("use test") tk.MustExec("drop table if exists t") tk.MustExec(`CREATE TABLE t ( @@ -466,11 +466,11 @@ func TestIssue20417(t *testing.T) { } func TestCaptureWithZeroSlowLogThreshold(t *testing.T) { - store, dom, clean := testkit.CreateMockStoreAndDomain(t) + store, clean := testkit.CreateMockStore(t) defer clean() tk := testkit.NewTestKit(t, store) - utilCleanBindingEnv(tk, dom) + tk.MustExec("use test") tk.MustExec("drop table if exists t") tk.MustExec("create table t(a int)") @@ -487,12 +487,12 @@ func TestCaptureWithZeroSlowLogThreshold(t *testing.T) { } func TestIssue25505(t *testing.T) { - store, dom, clean := testkit.CreateMockStoreAndDomain(t) + store, clean := testkit.CreateMockStore(t) defer clean() tk := testkit.NewTestKit(t, store) stmtsummary.StmtSummaryByDigestMap.Clear() - utilCleanBindingEnv(tk, dom) + tk.MustExec("use test") tk.MustExec("drop table if exists t") defer func() { @@ -561,7 +561,7 @@ func TestCaptureFilter(t *testing.T) { defer clean() tk := testkit.NewTestKit(t, store) - utilCleanBindingEnv(tk, dom) + stmtsummary.StmtSummaryByDigestMap.Clear() tk.MustExec(" set @@tidb_capture_plan_baselines = on") defer func() { From 13cee58a12505c7f72a33e35eff874505f766ccf Mon Sep 17 00:00:00 2001 From: senhtry Date: Wed, 22 Sep 2021 17:05:21 +0800 Subject: [PATCH 3/3] revert build flag --- testkit/testkit.go | 1 - 1 file changed, 1 deletion(-) diff --git a/testkit/testkit.go b/testkit/testkit.go index 1afa7d6035932..c2205d6b3a337 100644 --- a/testkit/testkit.go +++ b/testkit/testkit.go @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build !codes // +build !codes package testkit