diff --git a/pkg/bindinfo/BUILD.bazel b/pkg/bindinfo/BUILD.bazel index fc81b0958b6b9..17f2d6e9e36ea 100644 --- a/pkg/bindinfo/BUILD.bazel +++ b/pkg/bindinfo/BUILD.bazel @@ -6,7 +6,6 @@ go_library( "binding.go", "binding_cache.go", "binding_match.go", - "capture.go", "global_handle.go", "session_handle.go", "util.go", @@ -26,7 +25,6 @@ go_library( "//pkg/planner/core/resolve", "//pkg/sessionctx", "//pkg/sessionctx/sessionstates", - "//pkg/sessionctx/stmtctx", "//pkg/sessionctx/variable", "//pkg/types", "//pkg/types/parser_driver", @@ -40,9 +38,7 @@ go_library( "//pkg/util/memory", "//pkg/util/parser", "//pkg/util/sqlexec", - "//pkg/util/stmtsummary/v2:stmtsummary", "//pkg/util/stringutil", - "//pkg/util/table-filter", "@com_github_pingcap_errors//:errors", "@com_github_pingcap_failpoint//:failpoint", "@com_github_pkg_errors//:errors", @@ -57,7 +53,6 @@ go_test( srcs = [ "binding_cache_test.go", "binding_match_test.go", - "capture_test.go", "fuzzy_binding_test.go", "global_handle_test.go", "main_test.go", @@ -67,18 +62,14 @@ go_test( embed = [":bindinfo"], flaky = True, race = "on", - shard_count = 50, + shard_count = 33, deps = [ "//pkg/bindinfo/internal", "//pkg/bindinfo/norm", - "//pkg/config", - "//pkg/domain", - "//pkg/meta/model", "//pkg/parser", "//pkg/parser/ast", "//pkg/parser/auth", "//pkg/parser/format", - "//pkg/parser/model", "//pkg/parser/mysql", "//pkg/server", "//pkg/session/types", @@ -88,12 +79,10 @@ go_test( "//pkg/types", "//pkg/util", "//pkg/util/hack", - "//pkg/util/parser", "//pkg/util/stmtsummary", "@com_github_ngaut_pools//:pools", "@com_github_pingcap_failpoint//:failpoint", "@com_github_stretchr_testify//require", - "@io_opencensus_go//stats/view", "@org_uber_go_goleak//:goleak", ], ) diff --git a/pkg/bindinfo/binding.go b/pkg/bindinfo/binding.go index 5238fe23a71b0..42369778dcd60 100644 --- a/pkg/bindinfo/binding.go +++ b/pkg/bindinfo/binding.go @@ -41,8 +41,6 @@ const ( deleted = "deleted" // Manual indicates the binding is created by SQL like "create binding for ...". Manual = "manual" - // Capture indicates the binding is captured by TiDB automatically. - Capture = "capture" // Builtin indicates the binding is a builtin record for internal locking purpose. It is also the status for the builtin binding. Builtin = "builtin" // History indicate the binding is created from statement summary by plan digest diff --git a/pkg/bindinfo/capture.go b/pkg/bindinfo/capture.go deleted file mode 100644 index d98e5c563b470..0000000000000 --- a/pkg/bindinfo/capture.go +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2023 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 - -import ( - "strconv" - "strings" - - "github.com/pingcap/tidb/pkg/bindinfo/internal/logutil" - "github.com/pingcap/tidb/pkg/parser" - "github.com/pingcap/tidb/pkg/parser/ast" - "github.com/pingcap/tidb/pkg/sessionctx" - "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" - utilparser "github.com/pingcap/tidb/pkg/util/parser" - stmtsummaryv2 "github.com/pingcap/tidb/pkg/util/stmtsummary/v2" - tablefilter "github.com/pingcap/tidb/pkg/util/table-filter" - "go.uber.org/zap" -) - -type captureFilter struct { - frequency int64 - tables []tablefilter.Filter // `schema.table` - users map[string]struct{} - - fail bool - currentDB string -} - -func (cf *captureFilter) Enter(in ast.Node) (out ast.Node, skipChildren bool) { - if x, ok := in.(*ast.TableName); ok { - tblEntry := stmtctx.TableEntry{ - DB: x.Schema.L, - Table: x.Name.L, - } - if x.Schema.L == "" { - tblEntry.DB = cf.currentDB - } - for _, tableFilter := range cf.tables { - if tableFilter.MatchTable(tblEntry.DB, tblEntry.Table) { - cf.fail = true // some filter is matched - } - } - } - return in, cf.fail -} - -func (*captureFilter) Leave(in ast.Node) (out ast.Node, ok bool) { - return in, true -} - -func (cf *captureFilter) isEmpty() bool { - return len(cf.tables) == 0 && len(cf.users) == 0 -} - -// ParseCaptureTableFilter checks whether this filter is valid and parses it. -func ParseCaptureTableFilter(tableFilter string) (f tablefilter.Filter, valid bool) { - // forbid wildcards '!' and '@' for safety, - // please see https://github.com/pingcap/tidb-tools/tree/master/pkg/table-filter for more details. - tableFilter = strings.TrimLeft(tableFilter, " \t") - if tableFilter == "" { - return nil, false - } - if tableFilter[0] == '!' || tableFilter[0] == '@' { - return nil, false - } - var err error - f, err = tablefilter.Parse([]string{tableFilter}) - if err != nil { - return nil, false - } - return f, true -} - -func (h *globalBindingHandle) extractCaptureFilterFromStorage() (filter *captureFilter) { - filter = &captureFilter{ - frequency: 1, - users: make(map[string]struct{}), - } - - _ = h.callWithSCtx(false, func(sctx sessionctx.Context) error { - // No need to acquire the session context lock for ExecRestrictedSQL, it - // uses another background session. - rows, _, err := execRows(sctx, `SELECT filter_type, filter_value FROM mysql.capture_plan_baselines_blacklist order by filter_type`) - if err != nil { - logutil.BindLogger().Warn("failed to load mysql.capture_plan_baselines_blacklist", zap.Error(err)) - return err - } - for _, row := range rows { - filterTp := strings.ToLower(row.GetString(0)) - valStr := strings.ToLower(row.GetString(1)) - switch filterTp { - case "table": - tfilter, valid := ParseCaptureTableFilter(valStr) - if !valid { - logutil.BindLogger().Warn("capture table filter is invalid, ignore it", zap.String("filter_value", valStr)) - continue - } - filter.tables = append(filter.tables, tfilter) - case "user": - filter.users[valStr] = struct{}{} - case "frequency": - f, err := strconv.ParseInt(valStr, 10, 64) - if err != nil { - logutil.BindLogger().Warn("failed to parse frequency type value, ignore it", zap.String("filter_value", valStr), zap.Error(err)) - continue - } - if f < 1 { - logutil.BindLogger().Warn("frequency threshold is less than 1, ignore it", zap.Int64("frequency", f)) - continue - } - if f > filter.frequency { - filter.frequency = f - } - default: - logutil.BindLogger().Warn("unknown capture filter type, ignore it", zap.String("filter_type", filterTp)) - } - } - return nil - }) - return -} - -// CaptureBaselines is used to automatically capture plan baselines. -func (h *globalBindingHandle) CaptureBaselines() { - parser4Capture := parser.New() - captureFilter := h.extractCaptureFilterFromStorage() - emptyCaptureFilter := captureFilter.isEmpty() - bindableStmts := stmtsummaryv2.GetMoreThanCntBindableStmt(captureFilter.frequency) - for _, bindableStmt := range bindableStmts { - stmt, err := parser4Capture.ParseOneStmt(bindableStmt.Query, bindableStmt.Charset, bindableStmt.Collation) - if err != nil { - logutil.BindLogger().Debug("parse SQL failed in baseline capture", zap.String("SQL", bindableStmt.Query), zap.Error(err)) - continue - } - if insertStmt, ok := stmt.(*ast.InsertStmt); ok && insertStmt.Select == nil { - continue - } - if !emptyCaptureFilter { - captureFilter.fail = false - captureFilter.currentDB = bindableStmt.Schema - stmt.Accept(captureFilter) - if captureFilter.fail { - continue - } - - if len(captureFilter.users) > 0 { - filteredByUser := true - for user := range bindableStmt.Users { - if _, ok := captureFilter.users[user]; !ok { - filteredByUser = false // some user not in the black-list has processed this stmt - break - } - } - if filteredByUser { - continue - } - } - } - dbName := utilparser.GetDefaultDB(stmt, bindableStmt.Schema) - normalizedSQL, digest := parser.NormalizeDigest(utilparser.RestoreWithDefaultDB(stmt, dbName, bindableStmt.Query)) - if r := h.getCache().GetBinding(digest.String()); HasAvailableBinding(r) { - continue - } - bindSQL := GenerateBindingSQL(stmt, bindableStmt.PlanHint, true, dbName) - if bindSQL == "" { - continue - } - - var charset, collation string - _ = h.callWithSCtx(false, func(sctx sessionctx.Context) error { - charset, collation = sctx.GetSessionVars().GetCharsetInfo() - return nil - }) - binding := Binding{ - OriginalSQL: normalizedSQL, - Db: dbName, - BindSQL: bindSQL, - Status: Enabled, - Charset: charset, - Collation: collation, - Source: Capture, - SQLDigest: digest.String(), - } - // We don't need to pass the `sctx` because the BindSQL has been validated already. - err = h.CreateGlobalBinding(nil, []*Binding{&binding}) - if err != nil { - logutil.BindLogger().Debug("create bind record failed in baseline capture", zap.String("SQL", bindableStmt.Query), zap.Error(err)) - } - } -} diff --git a/pkg/bindinfo/capture_test.go b/pkg/bindinfo/capture_test.go deleted file mode 100644 index 86e4b12d5e5cb..0000000000000 --- a/pkg/bindinfo/capture_test.go +++ /dev/null @@ -1,986 +0,0 @@ -// 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 ( - "context" - "fmt" - "strings" - "testing" - "time" - - "github.com/pingcap/tidb/pkg/bindinfo" - "github.com/pingcap/tidb/pkg/bindinfo/internal" - "github.com/pingcap/tidb/pkg/bindinfo/norm" - "github.com/pingcap/tidb/pkg/config" - "github.com/pingcap/tidb/pkg/domain" - "github.com/pingcap/tidb/pkg/meta/model" - "github.com/pingcap/tidb/pkg/parser" - "github.com/pingcap/tidb/pkg/parser/auth" - pmodel "github.com/pingcap/tidb/pkg/parser/model" - "github.com/pingcap/tidb/pkg/testkit" - utilparser "github.com/pingcap/tidb/pkg/util/parser" - "github.com/pingcap/tidb/pkg/util/stmtsummary" - "github.com/stretchr/testify/require" - "go.opencensus.io/stats/view" -) - -func TestDMLCapturePlanBaseline(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec(" SET GLOBAL tidb_capture_plan_baselines = on") - defer func() { - tk.MustExec("SET GLOBAL 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.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, 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`), no_order_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`), no_order_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`), no_order_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`), no_order_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 := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("SET GLOBAL tidb_capture_plan_baselines = on") - defer func() { - tk.MustExec("SET GLOBAL 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.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, 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 TestCapturePlanBaseline4DisabledStatus(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - - internal.UtilCleanBindingEnv(tk, dom) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("SET GLOBAL tidb_capture_plan_baselines = on") - defer func() { - tk.MustExec("SET GLOBAL tidb_capture_plan_baselines = off") - }() - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, index idx_a(a))") - dom.BindHandle().CaptureBaselines() - tk.MustQuery("show global bindings").Check(testkit.Rows()) - tk.MustExec("select /*+ USE_INDEX(t, idx_a) */ * from t where a > 10") - tk.MustExec("select /*+ USE_INDEX(t, idx_a) */ * from t where a > 10") - tk.MustExec("admin capture bindings") - rows := tk.MustQuery("show global bindings").Rows() - require.Len(t, rows, 0) - - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, 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, bindinfo.Enabled, rows[0][3]) - require.Equal(t, bindinfo.Capture, rows[0][8]) - - tk.MustExec("select * from t where a > 10") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) - - tk.MustExec("set binding disabled for select * from t where a > 10") - - tk.MustExec("select * from t where a > 10") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) - rows = tk.MustQuery("show global bindings").Rows() - require.Len(t, rows, 1) - require.Equal(t, bindinfo.Disabled, rows[0][3]) - - 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, bindinfo.Disabled, rows[0][3]) - - tk.MustExec("select * from t where a > 10") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) - - tk.MustExec("drop global binding for select * from t where a > 10") - rows = tk.MustQuery("show global bindings").Rows() - require.Len(t, rows, 0) - - internal.UtilCleanBindingEnv(tk, dom) -} - -func TestCaptureDBCaseSensitivity(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - - 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.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, 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 := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("SET GLOBAL tidb_capture_plan_baselines = on") - defer func() { - tk.MustExec("SET GLOBAL 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.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, 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, bindinfo.Enabled, 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 := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - - stmtsummary.StmtSummaryByDigestMap.Clear() - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, 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))") - 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`), no_order_index(@`sel_1` `test`.`t` `idx_c`)*/ * FROM `test`.`t` WHERE `b` = ? AND `c` > ?", rows[0][1]) - - tk.MustUseIndex("select /*+ use_index(t,idx_b) */ * from t where b = 1 and c > 1", "idx_c(c)") - tk.MustExec("admin flush bindings") -} - -func TestCapturePlanBaselineIgnoreTiFlash(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - - 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.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, 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() - tbl, err := is.TableByName(context.Background(), pmodel.NewCIStr("test"), pmodel.NewCIStr("t")) - require.NoError(t, err) - tbl.Meta().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, "mpp[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 := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - - 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() - stmt, _, _ := internal.UtilNormalizeWithDefaultDB(t, "select * from t where a > ?") - _, fuzzyDigest := norm.NormalizeStmtForBinding(stmt, norm.WithFuzz(true)) - binding, matched := bindHandle.MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) - require.True(t, matched) - require.Equal(t, "select * from `test` . `t` where `a` > ?", binding.OriginalSQL) - require.Equal(t, bindinfo.Manual, binding.Source) - - // Test Source for captured sqls - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("SET GLOBAL tidb_capture_plan_baselines = on") - defer func() { - tk.MustExec("SET GLOBAL tidb_capture_plan_baselines = off") - }() - tk.MustExec("use test") - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, 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() - stmt, _, _ = internal.UtilNormalizeWithDefaultDB(t, "select * from t where a < ?") - _, fuzzyDigest = norm.NormalizeStmtForBinding(stmt, norm.WithFuzz(true)) - binding, matched = bindHandle.MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) - require.True(t, matched) - require.Equal(t, "select * from `test` . `t` where `a` < ?", binding.OriginalSQL) - require.Equal(t, bindinfo.Capture, binding.Source) -} - -func TestCapturedBindingCharset(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - - stmtsummary.StmtSummaryByDigestMap.Clear() - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, 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`), no_order_index(@`upd_1` `test`.`t` `idx`)*/ `test`.`t` SET `name`='hello' WHERE `name` <= 'abc'", rows[0][1]) - require.Equal(t, "utf8mb4", rows[0][6]) - require.Equal(t, "utf8mb4_bin", rows[0][7]) -} - -func TestConcurrentCapture(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - - // 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`', '', 'enabled', '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.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, 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 enabled", - )) -} - -func TestUpdateSubqueryCapture(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, c int, key idx_b(b))") - tk.MustExec("create table t2(a int, b int)") - stmtsummary.StmtSummaryByDigestMap.Clear() - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, 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 /*+ hash_join(`test`.`t2`@`sel_2`), hash_join(`test`.`t1`), use_index(@`upd_1` `test`.`t1` `idx_b`), no_order_index(@`upd_1` `test`.`t1` `idx_b`), use_index(@`sel_1` `test`.`t2` ), 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))" - originSQL := "UPDATE `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(originSQL) - 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 := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - - 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 - internal.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]) - tk.MustUseIndex("select * from t", "idxb(b)") - 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") - tk.MustUseIndex("SELECT /*+ use_index(@`sel_1` `test`.`t` `idxc`)*/ * FROM `test`.`t` WHERE `b`=2 AND `c`=3924541", "idxb(b)") - tk.MustUseIndex("SELECT /*+ use_index(@`sel_1` `test`.`t` `idxc`)*/ * FROM `t` WHERE `b`=2 AND `c`=3924541", "idxb(b)") - - // Test for capture baseline - internal.UtilCleanBindingEnv(tk, dom) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("SET GLOBAL tidb_capture_plan_baselines = on") - dom.BindHandle().CaptureBaselines() - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, 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`), no_order_index(@`sel_1` `test`.`t` `idxb`)*/ * FROM `test`.`t` WHERE `b` = 2 AND `c` = 213124", rows[0][1]) - tk.MustExec("SET GLOBAL tidb_capture_plan_baselines = off") -} - -func TestCaptureWithZeroSlowLogThreshold(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)") - stmtsummary.StmtSummaryByDigestMap.Clear() - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, 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 := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - stmtsummary.StmtSummaryByDigestMap.Clear() - - 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.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, 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 /*+ hash_agg(@`sel_1`), use_index(@`sel_3` `test`.`t1` `idx_ab`), no_order_index(@`sel_3` `test`.`t1` `idx_ab`)*/ * 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 /*+ hash_agg(@`sel_1`), use_index(@`sel_3` `test`.`t1` `idx_b`), no_order_index(@`sel_3` `test`.`t1` `idx_b`)*/ * 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`), order_index(@`sel_3` `test`.`t2` `idx_ab`), use_index(@`sel_1` `test`.`t1` `idx_ab`), order_index(@`sel_1` `test`.`t1` `idx_ab`)*/ * 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`), no_order_index(@`sel_3` `test`.`t2` `idx_a`), use_index(@`sel_1` `test`.`t1` `idx_a`), no_order_index(@`sel_1` `test`.`t1` `idx_a`)*/ * 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 TestCaptureUserFilter(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("SET GLOBAL tidb_capture_plan_baselines = on") - defer func() { - tk.MustExec("SET GLOBAL tidb_capture_plan_baselines = off") - }() - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int)") - - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, 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]) - - // test user filter - internal.UtilCleanBindingEnv(tk, dom) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('user', 'root')") - 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) // cannot capture the stmt - - // change another user - tk.MustExec(`create user usr1`) - tk.MustExec(`grant all on *.* to usr1 with grant option`) - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - require.NoError(t, tk2.Session().Auth(&auth.UserIdentity{Username: "usr1", Hostname: "%"}, nil, nil, nil)) - tk2.MustExec("select * from t where a > 10") - tk2.MustExec("select * from t where a > 10") - tk2.MustExec("admin capture bindings") - rows = tk2.MustQuery("show global bindings").Rows() - require.Len(t, rows, 1) // can capture the stmt - - // use user-filter with other types of filter together - internal.UtilCleanBindingEnv(tk, dom) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('user', 'root')") - tk.MustExec("insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('table', 'test.t')") - tk2.MustExec("select * from t where a > 10") - tk2.MustExec("select * from t where a > 10") - tk2.MustExec("admin capture bindings") - rows = tk2.MustQuery("show global bindings").Rows() - require.Len(t, rows, 0) // filtered by the table filter -} - -func TestCaptureTableFilterValid(t *testing.T) { - defer view.Stop() - type matchCase struct { - table string - matched bool - } - type filterCase struct { - filter string - valid bool - mcases []matchCase - } - filterCases := []filterCase{ - {"*.*", true, []matchCase{{"db.t", true}}}, - {"***.***", true, []matchCase{{"db.t", true}}}, - {"d*.*", true, []matchCase{{"db.t", true}}}, - {"*.t", true, []matchCase{{"db.t", true}}}, - {"?.t*", true, []matchCase{{"d.t", true}, {"d.tb", true}, {"db.t", false}}}, - {"db.t[1-3]", true, []matchCase{{"db.t1", true}, {"db.t2", true}, {"db.t4", false}}}, - {"!db.table", false, nil}, - {"@db.table", false, nil}, - {"table", false, nil}, - {"", false, nil}, - {"\t ", false, nil}, - } - for _, fc := range filterCases { - f, valid := bindinfo.ParseCaptureTableFilter(fc.filter) - require.Equal(t, fc.valid, valid) - if valid { - for _, mc := range fc.mcases { - tmp := strings.Split(mc.table, ".") - require.Equal(t, mc.matched, f.MatchTable(tmp[0], tmp[1])) - } - } - } -} - -func TestCaptureWildcardFilter(t *testing.T) { - t.Skip("the old implementation of Capture is considered as deprecated") - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("SET GLOBAL tidb_capture_plan_baselines = on") - defer func() { - tk.MustExec("SET GLOBAL tidb_capture_plan_baselines = off") - }() - - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - dbs := []string{"db11", "db12", "db2"} - tbls := []string{"t11", "t12", "t2"} - for _, db := range dbs { - tk.MustExec(fmt.Sprintf(`drop database if exists %v`, db)) - tk.MustExec(fmt.Sprintf(`create database %v`, db)) - tk.MustExec(fmt.Sprintf(`use %v`, db)) - for _, tbl := range tbls { - tk.MustExec(fmt.Sprintf(`create table %v(a int)`, tbl)) - } - } - mustExecTwice := func() { - for _, db := range dbs { - for _, tbl := range tbls { - tk.MustExec(fmt.Sprintf(`select * from %v.%v where a>10`, db, tbl)) - tk.MustExec(fmt.Sprintf(`select * from %v.%v where a>10`, db, tbl)) - } - } - } - checkBindings := func(dbTbls ...string) { - m := make(map[string]bool) // map[query]existed - for _, dbTbl := range dbTbls { - tmp := strings.Split(dbTbl, ".") - q := fmt.Sprintf("select * from `%v` . `%v` where `a` > ?", tmp[0], tmp[1]) - m[q] = false - } - - tk.MustExec("admin capture bindings") - var rows [][]any - require.Eventually(t, func() bool { - rows = tk.MustQuery("show global bindings").Sort().Rows() - return len(rows) == len(dbTbls) - }, time.Second*2, time.Millisecond*100) - for _, r := range rows { - q := r[0].(string) - if _, exist := m[q]; !exist { // encounter an unexpected binding - t.Fatalf("unexpected binding %v", q) - } - m[q] = true - } - for q, exist := range m { - if !exist { // a expected binding is not existed - t.Fatalf("missed binding %v", q) - } - } - } - - internal.UtilCleanBindingEnv(tk, dom) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec(`insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('table', 'db11.t1*')`) - mustExecTwice() - checkBindings("db11.t2", "db12.t11", "db12.t12", "db12.t2", "db2.t11", "db2.t12", "db2.t2") // db11.t11 and db11.t12 are filtered - - internal.UtilCleanBindingEnv(tk, dom) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("delete from mysql.capture_plan_baselines_blacklist") - tk.MustExec(`insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('table', 'db1*.t11')`) - mustExecTwice() - checkBindings("db11.t12", "db11.t2", "db12.t12", "db12.t2", "db2.t11", "db2.t12", "db2.t2") // db11.t11 and db12.t11 are filtered - - internal.UtilCleanBindingEnv(tk, dom) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("delete from mysql.capture_plan_baselines_blacklist") - tk.MustExec(`insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('table', 'db1*.t1*')`) - mustExecTwice() - checkBindings("db11.t2", "db12.t2", "db2.t11", "db2.t12", "db2.t2") // db11.t11 / db12.t11 / db11.t12 / db12.t12 are filtered - - internal.UtilCleanBindingEnv(tk, dom) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("delete from mysql.capture_plan_baselines_blacklist") - tk.MustExec(`insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('table', 'db1*.*')`) - mustExecTwice() - checkBindings("db2.t11", "db2.t12", "db2.t2") // db11.* / db12.* are filtered - - internal.UtilCleanBindingEnv(tk, dom) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("delete from mysql.capture_plan_baselines_blacklist") - tk.MustExec(`insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('table', '*.t1*')`) - mustExecTwice() - checkBindings("db11.t2", "db12.t2", "db2.t2") // *.t11 and *.t12 are filtered - - internal.UtilCleanBindingEnv(tk, dom) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("delete from mysql.capture_plan_baselines_blacklist") - tk.MustExec(`insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('table', 'db*.t*')`) - mustExecTwice() - checkBindings() // all are filtered - - internal.UtilCleanBindingEnv(tk, dom) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("delete from mysql.capture_plan_baselines_blacklist") - mustExecTwice() - checkBindings("db11.t11", "db11.t12", "db11.t2", "db12.t11", "db12.t12", "db12.t2", "db2.t11", "db2.t12", "db2.t2") // no filter, all can be captured -} - -func TestCaptureFilter(t *testing.T) { - t.Skip("the old implementation of Capture is considered as deprecated") - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("SET GLOBAL tidb_capture_plan_baselines = on") - defer func() { - tk.MustExec("SET GLOBAL tidb_capture_plan_baselines = off") - }() - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int)") - - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, 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. - internal.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. - internal.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. - internal.UtilCleanBindingEnv(tk, dom) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('table', '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. - internal.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. - internal.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. - internal.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. - internal.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]) - - internal.UtilCleanBindingEnv(tk, dom) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("insert into mysql.capture_plan_baselines_blacklist(filter_type, filter_value) values('table', '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]) -} - -func TestCaptureHints(t *testing.T) { - t.Skip("deprecated") - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("SET GLOBAL tidb_capture_plan_baselines = on") - defer func() { - tk.MustExec("SET GLOBAL tidb_capture_plan_baselines = off") - }() - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(pk int primary key, a int, b int, key(a), key(b))") - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - - captureCases := []struct { - query string - hint string - }{ - // agg hints - {"select /*+ hash_agg() */ count(1) from t", "hash_agg"}, - {"select /*+ stream_agg() */ count(1) from t", "stream_agg"}, - // join hints - {"select /*+ merge_join(t1, t2) */ * from t t1, t t2 where t1.a=t2.a", "merge_join"}, - {"select /*+ tidb_smj(t1, t2) */ * from t t1, t t2 where t1.a=t2.a", "merge_join"}, - {"select /*+ hash_join(t1, t2) */ * from t t1, t t2 where t1.a=t2.a", "hash_join"}, - {"select /*+ tidb_hj(t1, t2) */ * from t t1, t t2 where t1.a=t2.a", "hash_join"}, - {"select /*+ inl_join(t1, t2) */ * from t t1, t t2 where t1.a=t2.a", "inl_join"}, - {"select /*+ tidb_inlj(t1, t2) */ * from t t1, t t2 where t1.a=t2.a", "inl_join"}, - {"select /*+ inl_hash_join(t1, t2) */ * from t t1, t t2 where t1.a=t2.a", "inl_hash_join"}, - // index hints - {"select * from t use index(primary)", "use_index(@`sel_1` `test`.`t` )"}, - {"select /*+ use_index(primary) */ * from t", "use_index(@`sel_1` `test`.`t` )"}, - {"select * from t use index(a)", "use_index(@`sel_1` `test`.`t` `a`)"}, - {"select /*+ use_index(a) */ * from t use index(a)", "use_index(@`sel_1` `test`.`t` `a`)"}, - {"select * from t use index(b)", "use_index(@`sel_1` `test`.`t` `b`)"}, - {"select /*+ use_index(b) */ * from t use index(b)", "use_index(@`sel_1` `test`.`t` `b`)"}, - {"select /*+ use_index_merge(t, a, b) */ a, b from t where a=1 or b=1", "use_index_merge(@`sel_1` `t` `a`, `b`)"}, - {"select /*+ ignore_index(t, a) */ * from t where a=1", "ignore_index(`t` `a`)"}, - // push-down hints - {"select /*+ limit_to_cop() */ * from t limit 10", "limit_to_cop(@`sel_1`)"}, - {"select /*+ agg_to_cop() */ a, count(*) from t group by a", "agg_to_cop(@`sel_1`)"}, - // index-merge hints - {"select /*+ no_index_merge() */ a, b from t where a>1 or b>1", "no_index_merge()"}, - {"select /*+ use_index_merge(t, a, b) */ a, b from t where a>1 or b>1", "use_index_merge(@`sel_1` `t` `a`, `b`)"}, - // runtime hints - {"select /*+ memory_quota(1024 MB) */ * from t", "memory_quota(1024 mb)"}, - {"select /*+ max_execution_time(1000) */ * from t", "max_execution_time(1000)"}, - // storage hints - {"select /*+ read_from_storage(tikv[t]) */ * from t", "read_from_storage(tikv[`t`])"}, - // others - {"select /*+ use_toja(true) */ t1.a, t1.b from t t1 where t1.a in (select t2.a from t t2)", "use_toja(true)"}, - } - for _, capCase := range captureCases { - stmtsummary.StmtSummaryByDigestMap.Clear() - internal.UtilCleanBindingEnv(tk, dom) - tk.MustExec(capCase.query) - tk.MustExec(capCase.query) - tk.MustExec("admin capture bindings") - res := tk.MustQuery(`show global bindings`).Rows() - require.Equal(t, len(res), 1) // this query is captured, and - require.True(t, strings.Contains(res[0][1].(string), capCase.hint), fmt.Sprintf("%v:%v", capCase.query, res[0][1])) // the binding contains the expected hint - // test sql digest - parser4binding := parser.New() - originNode, err := parser4binding.ParseOneStmt(capCase.query, "utf8mb4", "utf8mb4_general_ci") - require.NoError(t, err) - _, sqlDigestWithDB := parser.NormalizeDigest(utilparser.RestoreWithDefaultDB(originNode, "test", capCase.query)) - require.Equal(t, res[0][9], sqlDigestWithDB.String()) - } -} diff --git a/pkg/bindinfo/global_handle.go b/pkg/bindinfo/global_handle.go index d861eb62eb425..dac1b4a868132 100644 --- a/pkg/bindinfo/global_handle.go +++ b/pkg/bindinfo/global_handle.go @@ -84,11 +84,6 @@ type GlobalBindingHandle interface { // GetMemCapacity returns the memory capacity for the bind cache. GetMemCapacity() (memCapacity int64) - // Methods for Auto Capture. - - // CaptureBaselines is used to automatically capture plan baselines. - CaptureBaselines() - variable.Statistics } diff --git a/pkg/bindinfo/session_handle_test.go b/pkg/bindinfo/session_handle_test.go index 61377ceee62f4..ae1b4af53e08a 100644 --- a/pkg/bindinfo/session_handle_test.go +++ b/pkg/bindinfo/session_handle_test.go @@ -138,97 +138,6 @@ func TestSessionBinding(t *testing.T) { } } -func TestBaselineDBLowerCase(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - - 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)") - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - tk.MustExec("update t set a = a + 1") - tk.MustExec("update t set a = a + 1") - tk.MustExec("admin capture bindings") - rows := tk.MustQuery("show global bindings").Rows() - require.Len(t, rows, 1) - require.Equal(t, "update `spm` . `t` set `a` = `a` + ?", rows[0][0]) - // default_db should have lower case. - require.Equal(t, "spm", rows[0][2]) - tk.MustExec("drop global binding for update t set a = a + 1") - rows = tk.MustQuery("show global bindings").Rows() - // DROP GLOBAL BINGING should remove the binding even if we are in SPM database. - require.Len(t, rows, 0) - - tk.MustExec("create global binding for select * from t using select * from t") - rows = tk.MustQuery("show global bindings").Rows() - require.Len(t, rows, 1) - require.Equal(t, "select * from `spm` . `t`", rows[0][0]) - // default_db should have lower case. - require.Equal(t, "spm", rows[0][2]) - tk.MustExec("drop global binding for select * from t") - rows = tk.MustQuery("show global bindings").Rows() - // DROP GLOBAL BINGING should remove the binding even if we are in SPM database. - require.Len(t, rows, 0) - - tk.MustExec("create session binding for select * from t using select * from t") - rows = tk.MustQuery("show session bindings").Rows() - require.Len(t, rows, 1) - require.Equal(t, "select * from `spm` . `t`", rows[0][0]) - // default_db should have lower case. - require.Equal(t, "spm", rows[0][2]) - tk.MustExec("drop session binding for select * from t") - rows = tk.MustQuery("show session bindings").Rows() - // DROP SESSION BINGING should remove the binding even if we are in SPM database. - require.Len(t, rows, 0) - - internal.UtilCleanBindingEnv(tk, dom) - - // Simulate existing bindings with upper case default_db. - _, sqlDigest := parser.NormalizeDigestForBinding("select * from `spm` . `t`") - tk.MustExec("insert into mysql.bind_info values('select * from `spm` . `t`', 'select * from `spm` . `t`', 'SPM', 'enabled', '2000-01-01 09:00:00', '2000-01-01 09:00:00', '', '','" + - bindinfo.Manual + "', '" + sqlDigest.String() + "', '')") - tk.MustQuery("select original_sql, default_db from mysql.bind_info where original_sql = 'select * from `spm` . `t`'").Check(testkit.Rows( - "select * from `spm` . `t` SPM", - )) - tk.MustExec("admin reload bindings") - rows = tk.MustQuery("show global bindings").Rows() - require.Len(t, rows, 1) - require.Equal(t, "select * from `spm` . `t`", rows[0][0]) - // default_db should have lower case. - require.Equal(t, "spm", rows[0][2]) - tk.MustExec("drop global binding for select * from t") - rows = tk.MustQuery("show global bindings").Rows() - // DROP GLOBAL BINGING should remove the binding even if we are in SPM database. - require.Len(t, rows, 0) - - internal.UtilCleanBindingEnv(tk, dom) - // Simulate existing bindings with upper case default_db. - tk.MustExec("insert into mysql.bind_info values('select * from `spm` . `t`', 'select * from `spm` . `t`', 'SPM', 'enabled', '2000-01-01 09:00:00', '2000-01-01 09:00:00', '', '','" + - bindinfo.Manual + "', '" + sqlDigest.String() + "', '')") - tk.MustQuery("select original_sql, default_db from mysql.bind_info where original_sql = 'select * from `spm` . `t`'").Check(testkit.Rows( - "select * from `spm` . `t` SPM", - )) - tk.MustExec("admin reload bindings") - rows = tk.MustQuery("show global bindings").Rows() - require.Len(t, rows, 1) - require.Equal(t, "select * from `spm` . `t`", rows[0][0]) - // default_db should have lower case. - require.Equal(t, "spm", rows[0][2]) - tk.MustExec("create global binding for select * from t using select * from t") - rows = tk.MustQuery("show global bindings").Rows() - require.Len(t, rows, 1) - require.Equal(t, "select * from `spm` . `t`", rows[0][0]) - // default_db should have lower case. - require.Equal(t, "spm", rows[0][2]) - tk.MustQuery("select original_sql, default_db, status from mysql.bind_info where original_sql = 'select * from `spm` . `t`'").Check(testkit.Rows( - "select * from `spm` . `t` SPM deleted", - "select * from `spm` . `t` spm enabled", - )) -} - func TestShowGlobalBindings(t *testing.T) { store := testkit.CreateMockStore(t) diff --git a/pkg/domain/domain.go b/pkg/domain/domain.go index 57833388edfa5..2ac4ebda71bcc 100644 --- a/pkg/domain/domain.go +++ b/pkg/domain/domain.go @@ -2134,11 +2134,6 @@ func (do *Domain) globalBindHandleWorkerLoop(owner owner.Manager) { if err != nil { logutil.BgLogger().Error("update bindinfo failed", zap.Error(err)) } - // Get Global - optVal, err := do.GetGlobalVar(variable.TiDBCapturePlanBaseline) - if err == nil && variable.TiDBOptOn(optVal) { - bindHandle.CaptureBaselines() - } case <-gcBindTicker.C: if !owner.IsOwner() { continue diff --git a/pkg/executor/bind.go b/pkg/executor/bind.go index 09894b82dd31d..cc6ac1fce7481 100644 --- a/pkg/executor/bind.go +++ b/pkg/executor/bind.go @@ -47,8 +47,6 @@ func (e *SQLBindExec) Next(_ context.Context, req *chunk.Chunk) error { return e.dropSQLBindByDigest() case plannercore.OpFlushBindings: return e.flushBindings() - case plannercore.OpCaptureBindings: - e.captureBindings() case plannercore.OpReloadBindings: return e.reloadBindings() case plannercore.OpSetBindingStatus: @@ -58,7 +56,6 @@ func (e *SQLBindExec) Next(_ context.Context, req *chunk.Chunk) error { default: return errors.Errorf("unsupported SQL bind operation: %v", e.sqlBindOp) } - return nil } func (e *SQLBindExec) dropSQLBind() error { @@ -160,10 +157,6 @@ func (e *SQLBindExec) flushBindings() error { return domain.GetDomain(e.Ctx()).BindHandle().LoadFromStorageToCache(false) } -func (e *SQLBindExec) captureBindings() { - domain.GetDomain(e.Ctx()).BindHandle().CaptureBaselines() -} - func (e *SQLBindExec) reloadBindings() error { return domain.GetDomain(e.Ctx()).BindHandle().LoadFromStorageToCache(true) } diff --git a/pkg/infoschema/test/clustertablestest/tables_test.go b/pkg/infoschema/test/clustertablestest/tables_test.go index 9c3b62d1e1223..14eca9a4cf085 100644 --- a/pkg/infoschema/test/clustertablestest/tables_test.go +++ b/pkg/infoschema/test/clustertablestest/tables_test.go @@ -974,61 +974,6 @@ func TestStmtSummaryTablePrivilege(t *testing.T) { require.Equal(t, 2, len(result.Rows())) } -func TestCapturePrivilege(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := newTestKitWithRoot(t, store) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b varchar(10), key k(a))") - defer tk.MustExec("drop table if exists t") - - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1(a int, b varchar(10), key k(a))") - defer tk.MustExec("drop table if exists t1") - - // Disable refreshing summary. - tk.MustExec("set global tidb_stmt_summary_refresh_interval = 999999999") - tk.MustQuery("select @@global.tidb_stmt_summary_refresh_interval").Check(testkit.Rows("999999999")) - // Clear all statements. - tk.MustExec("set global tidb_enable_stmt_summary = 0") - tk.MustExec("set global tidb_enable_stmt_summary = 1") - - // Create a new user to test statements summary table privilege - tk.MustExec("drop user if exists 'test_user'@'localhost'") - tk.MustExec("create user 'test_user'@'localhost'") - defer tk.MustExec("drop user if exists 'test_user'@'localhost'") - tk.MustExec("grant select on test.t1 to 'test_user'@'localhost'") - tk.MustExec("select * from t where a=1") - tk.MustExec("select * from t where a=1") - tk.MustExec("admin capture bindings") - rows := tk.MustQuery("show global bindings").Rows() - require.Len(t, rows, 1) - - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - tk1.Session().Auth(&auth.UserIdentity{ - Username: "test_user", - Hostname: "localhost", - AuthUsername: "test_user", - AuthHostname: "localhost", - }, nil, nil, nil) - - rows = tk1.MustQuery("show global bindings").Rows() - // Ordinary users can not see others' records - require.Len(t, rows, 0) - tk1.MustExec("select * from t1 where b=1") - tk1.MustExec("select * from t1 where b=1") - tk1.MustExec("admin capture bindings") - rows = tk1.MustQuery("show global bindings").Rows() - require.Len(t, rows, 1) - - tk.MustExec("grant all on *.* to 'test_user'@'localhost'") - tk1.MustExec("admin capture bindings") - rows = tk1.MustQuery("show global bindings").Rows() - require.Len(t, rows, 2) -} - // TestStmtSummaryInternalQuery Test statements_summary_history. func TestStmtSummaryInternalQuery(t *testing.T) { store := testkit.CreateMockStore(t) diff --git a/pkg/planner/core/planbuilder.go b/pkg/planner/core/planbuilder.go index 1a9fc6af3dfbc..dab1ada9021a7 100644 --- a/pkg/planner/core/planbuilder.go +++ b/pkg/planner/core/planbuilder.go @@ -1565,7 +1565,7 @@ func (b *PlanBuilder) buildAdmin(ctx context.Context, as *ast.AdminStmt) (base.P case ast.AdminFlushBindings: return &SQLBindPlan{SQLBindOp: OpFlushBindings}, nil case ast.AdminCaptureBindings: - return &SQLBindPlan{SQLBindOp: OpCaptureBindings}, nil + return nil, errors.Errorf("Auto Capture is not supported") case ast.AdminEvolveBindings: return nil, errors.Errorf("Cannot enable baseline evolution feature, it is not generally available now") case ast.AdminReloadBindings: diff --git a/pkg/util/stmtsummary/v2/tests/BUILD.bazel b/pkg/util/stmtsummary/v2/tests/BUILD.bazel index acf8e2e8415cf..9d85a25a428b6 100644 --- a/pkg/util/stmtsummary/v2/tests/BUILD.bazel +++ b/pkg/util/stmtsummary/v2/tests/BUILD.bazel @@ -8,7 +8,7 @@ go_test( "table_test.go", ], flaky = True, - shard_count = 14, + shard_count = 13, deps = [ "//pkg/config", "//pkg/kv", diff --git a/pkg/util/stmtsummary/v2/tests/table_test.go b/pkg/util/stmtsummary/v2/tests/table_test.go index fa4b313712f40..ae7980ade2efb 100644 --- a/pkg/util/stmtsummary/v2/tests/table_test.go +++ b/pkg/util/stmtsummary/v2/tests/table_test.go @@ -314,59 +314,6 @@ func TestStmtSummaryTablePrivilege(t *testing.T) { require.Equal(t, 2, len(result.Rows())) } -func TestCapturePrivilege(t *testing.T) { - setupStmtSummary() - defer closeStmtSummary() - - store := testkit.CreateMockStore(t) - tk := newTestKitWithRoot(t, store) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b varchar(10), key k(a))") - defer tk.MustExec("drop table if exists t") - - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1(a int, b varchar(10), key k(a))") - defer tk.MustExec("drop table if exists t1") - - // Clear all statements. - tk.MustExec("set global tidb_enable_stmt_summary = 0") - tk.MustExec("set global tidb_enable_stmt_summary = 1") - - // Create a new user to test statements summary table privilege - tk.MustExec("drop user if exists 'test_user'@'localhost'") - tk.MustExec("create user 'test_user'@'localhost'") - defer tk.MustExec("drop user if exists 'test_user'@'localhost'") - tk.MustExec("grant select on test.t1 to 'test_user'@'localhost'") - tk.MustExec("select * from t where a=1") - tk.MustExec("select * from t where a=1") - tk.MustExec("admin capture bindings") - rows := tk.MustQuery("show global bindings").Rows() - require.Len(t, rows, 1) - - tk1 := newTestKit(t, store) - tk1.Session().Auth(&auth.UserIdentity{ - Username: "test_user", - Hostname: "localhost", - AuthUsername: "test_user", - AuthHostname: "localhost", - }, nil, nil, nil) - - rows = tk1.MustQuery("show global bindings").Rows() - // Ordinary users can not see others' records - require.Len(t, rows, 0) - tk1.MustExec("select * from t1 where b=1") - tk1.MustExec("select * from t1 where b=1") - tk1.MustExec("admin capture bindings") - rows = tk1.MustQuery("show global bindings").Rows() - require.Len(t, rows, 1) - - tk.MustExec("grant all on *.* to 'test_user'@'localhost'") - tk1.MustExec("admin capture bindings") - rows = tk1.MustQuery("show global bindings").Rows() - require.Len(t, rows, 2) -} - func TestStmtSummaryErrorCount(t *testing.T) { setupStmtSummary() defer closeStmtSummary()