diff --git a/pkg/bindinfo/BUILD.bazel b/pkg/bindinfo/BUILD.bazel index 8b72789cab467..8a24a48b0cbb6 100644 --- a/pkg/bindinfo/BUILD.bazel +++ b/pkg/bindinfo/BUILD.bazel @@ -14,6 +14,7 @@ go_library( importpath = "github.com/pingcap/tidb/pkg/bindinfo", visibility = ["//visibility:public"], deps = [ + "//pkg/bindinfo/norm", "//pkg/kv", "//pkg/metrics", "//pkg/parser", @@ -63,6 +64,7 @@ go_test( shard_count = 50, deps = [ "//pkg/bindinfo/internal", + "//pkg/bindinfo/norm", "//pkg/config", "//pkg/domain", "//pkg/metrics", diff --git a/pkg/bindinfo/binding_match.go b/pkg/bindinfo/binding_match.go index b0409a4f39f83..209ade24c3e2f 100644 --- a/pkg/bindinfo/binding_match.go +++ b/pkg/bindinfo/binding_match.go @@ -15,15 +15,13 @@ package bindinfo import ( - "strings" "sync" + "github.com/pingcap/tidb/pkg/bindinfo/norm" "github.com/pingcap/tidb/pkg/metrics" - "github.com/pingcap/tidb/pkg/parser" "github.com/pingcap/tidb/pkg/parser/ast" "github.com/pingcap/tidb/pkg/sessionctx" "github.com/pingcap/tidb/pkg/util/hint" - utilparser "github.com/pingcap/tidb/pkg/util/parser" ) var ( @@ -74,7 +72,7 @@ func getBindRecord(sctx sessionctx.Context, stmtNode ast.StmtNode, info *Binding var fuzzyDigest string var tableNames []*ast.TableName if info == nil || info.TableNames == nil || info.FuzzyDigest == "" { - _, fuzzyDigest = NormalizeStmtForFuzzyBinding(stmtNode) + _, fuzzyDigest = norm.NormalizeStmtForBinding(stmtNode, norm.WithFuzz(true)) tableNames = CollectTableNames(stmtNode) if info != nil { info.FuzzyDigest = fuzzyDigest @@ -99,90 +97,6 @@ func getBindRecord(sctx sessionctx.Context, stmtNode ast.StmtNode, info *Binding return nil, "" } -func eraseLastSemicolon(stmt ast.StmtNode) { - sql := stmt.Text() - if len(sql) > 0 && sql[len(sql)-1] == ';' { - stmt.SetText(nil, sql[:len(sql)-1]) - } -} - -// NormalizeStmtForBinding normalizes a statement for binding. -// Schema names will be completed automatically: `select * from t` --> `select * from db . t`. -func NormalizeStmtForBinding(stmtNode ast.StmtNode, specifiedDB string) (normalizedStmt, exactSQLDigest string) { - return normalizeStmt(stmtNode, specifiedDB, false) -} - -// NormalizeStmtForFuzzyBinding normalizes a statement for fuzzy matching. -// Schema names will be eliminated automatically: `select * from db . t` --> `select * from t`. -func NormalizeStmtForFuzzyBinding(stmtNode ast.StmtNode) (normalizedStmt, fuzzySQLDigest string) { - return normalizeStmt(stmtNode, "", true) -} - -// NormalizeStmtForBinding normalizes a statement for binding. -// This function skips Explain automatically, and literals in in-lists will be normalized as '...'. -// For normal bindings, DB name will be completed automatically: -// -// e.g. `select * from t where a in (1, 2, 3)` --> `select * from test.t where a in (...)` -func normalizeStmt(stmtNode ast.StmtNode, specifiedDB string, fuzzy bool) (normalizedStmt, sqlDigest string) { - normalize := func(n ast.StmtNode) (normalizedStmt, sqlDigest string) { - eraseLastSemicolon(n) - var digest *parser.Digest - var normalizedSQL string - if !fuzzy { - normalizedSQL = utilparser.RestoreWithDefaultDB(n, specifiedDB, n.Text()) - } else { - normalizedSQL = utilparser.RestoreWithoutDB(n) - } - normalizedStmt, digest = parser.NormalizeDigestForBinding(normalizedSQL) - return normalizedStmt, digest.String() - } - - switch x := stmtNode.(type) { - case *ast.ExplainStmt: - // This function is only used to find bind record. - // For some SQLs, such as `explain select * from t`, they will be entered here many times, - // but some of them do not want to obtain bind record. - // The difference between them is whether len(x.Text()) is empty. They cannot be distinguished by stmt.restore. - // For these cases, we need return "" as normalize SQL and hash. - if len(x.Text()) == 0 { - return "", "" - } - switch x.Stmt.(type) { - case *ast.SelectStmt, *ast.DeleteStmt, *ast.UpdateStmt, *ast.InsertStmt: - normalizeSQL, digest := normalize(x.Stmt) - return normalizeSQL, digest - case *ast.SetOprStmt: - normalizeExplainSQL, _ := normalize(x) - - idx := strings.Index(normalizeExplainSQL, "select") - parenthesesIdx := strings.Index(normalizeExplainSQL, "(") - if parenthesesIdx != -1 && parenthesesIdx < idx { - idx = parenthesesIdx - } - // If the SQL is `EXPLAIN ((VALUES ROW ()) ORDER BY 1);`, the idx will be -1. - if idx == -1 { - hash := parser.DigestNormalized(normalizeExplainSQL) - return normalizeExplainSQL, hash.String() - } - normalizeSQL := normalizeExplainSQL[idx:] - hash := parser.DigestNormalized(normalizeSQL) - return normalizeSQL, hash.String() - } - case *ast.SelectStmt, *ast.SetOprStmt, *ast.DeleteStmt, *ast.UpdateStmt, *ast.InsertStmt: - // This function is only used to find bind record. - // For some SQLs, such as `explain select * from t`, they will be entered here many times, - // but some of them do not want to obtain bind record. - // The difference between them is whether len(x.Text()) is empty. They cannot be distinguished by stmt.restore. - // For these cases, we need return "" as normalize SQL and hash. - if len(x.Text()) == 0 { - return "", "" - } - normalizedSQL, digest := normalize(x) - return normalizedSQL, digest - } - return "", "" -} - func fuzzyMatchBindingTableName(currentDB string, stmtTableNames, bindingTableNames []*ast.TableName) (numWildcards int, matched bool) { if len(stmtTableNames) != len(bindingTableNames) { return 0, false diff --git a/pkg/bindinfo/capture_test.go b/pkg/bindinfo/capture_test.go index 13db280c4bc12..7def42d711297 100644 --- a/pkg/bindinfo/capture_test.go +++ b/pkg/bindinfo/capture_test.go @@ -22,6 +22,7 @@ import ( "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/parser" @@ -329,7 +330,7 @@ func TestBindingSource(t *testing.T) { 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 := bindinfo.NormalizeStmtForFuzzyBinding(stmt) + _, fuzzyDigest := norm.NormalizeStmtForBinding(stmt, norm.WithFuzz(true)) bindData, err := bindHandle.MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) require.NoError(t, err) require.NotNil(t, bindData) @@ -351,7 +352,7 @@ func TestBindingSource(t *testing.T) { tk.MustExec("admin capture bindings") bindHandle.CaptureBaselines() stmt, _, _ = internal.UtilNormalizeWithDefaultDB(t, "select * from t where a < ?") - _, fuzzyDigest = bindinfo.NormalizeStmtForFuzzyBinding(stmt) + _, fuzzyDigest = norm.NormalizeStmtForBinding(stmt, norm.WithFuzz(true)) bindData, err = bindHandle.MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) require.NoError(t, err) require.NotNil(t, bindData) diff --git a/pkg/bindinfo/global_handle.go b/pkg/bindinfo/global_handle.go index d14b6518a63cf..687306117acce 100644 --- a/pkg/bindinfo/global_handle.go +++ b/pkg/bindinfo/global_handle.go @@ -24,6 +24,7 @@ import ( "time" "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/bindinfo/norm" "github.com/pingcap/tidb/pkg/metrics" "github.com/pingcap/tidb/pkg/parser" "github.com/pingcap/tidb/pkg/parser/ast" @@ -189,7 +190,7 @@ func buildFuzzyDigestMap(bindRecords []*BindRecord) map[string][]string { p = parser.New() continue } - _, fuzzyDigest := NormalizeStmtForFuzzyBinding(stmt) + _, fuzzyDigest := norm.NormalizeStmtForBinding(stmt, norm.WithFuzz(true)) m[fuzzyDigest] = append(m[fuzzyDigest], binding.SQLDigest) } } diff --git a/pkg/bindinfo/global_handle_test.go b/pkg/bindinfo/global_handle_test.go index e813fd1f9be0f..e21af9b68f343 100644 --- a/pkg/bindinfo/global_handle_test.go +++ b/pkg/bindinfo/global_handle_test.go @@ -22,6 +22,7 @@ import ( "github.com/ngaut/pools" "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/metrics" "github.com/pingcap/tidb/pkg/parser" sessiontypes "github.com/pingcap/tidb/pkg/session/types" @@ -70,7 +71,7 @@ func TestBindingLastUpdateTime(t *testing.T) { stmt, err := parser.New().ParseOneStmt("select * from test . t0", "", "") require.NoError(t, err) - _, fuzzyDigest := bindinfo.NormalizeStmtForFuzzyBinding(stmt) + _, fuzzyDigest := norm.NormalizeStmtForBinding(stmt, norm.WithFuzz(true)) bindData, err := bindHandle.MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) require.NoError(t, err) require.Equal(t, 1, len(bindData.Bindings)) @@ -139,7 +140,7 @@ func TestBindParse(t *testing.T) { stmt, err := parser.New().ParseOneStmt("select * from test . t", "", "") require.NoError(t, err) - _, fuzzyDigest := bindinfo.NormalizeStmtForFuzzyBinding(stmt) + _, fuzzyDigest := norm.NormalizeStmtForBinding(stmt, norm.WithFuzz(true)) bindData, err := bindHandle.MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) require.NoError(t, err) require.NotNil(t, bindData) @@ -441,7 +442,7 @@ func TestGlobalBinding(t *testing.T) { stmt, _, _ := internal.UtilNormalizeWithDefaultDB(t, testSQL.querySQL) - _, fuzzyDigest := bindinfo.NormalizeStmtForFuzzyBinding(stmt) + _, fuzzyDigest := norm.NormalizeStmtForBinding(stmt, norm.WithFuzz(true)) bindData, err := dom.BindHandle().MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) require.NoError(t, err) require.NotNil(t, bindData) @@ -476,7 +477,7 @@ func TestGlobalBinding(t *testing.T) { require.NoError(t, err) require.Equal(t, 1, bindHandle.Size()) - _, fuzzyDigest = bindinfo.NormalizeStmtForFuzzyBinding(stmt) + _, fuzzyDigest = norm.NormalizeStmtForBinding(stmt, norm.WithFuzz(true)) bindData, err = dom.BindHandle().MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) require.NoError(t, err) require.NotNil(t, bindData) @@ -493,7 +494,7 @@ func TestGlobalBinding(t *testing.T) { _, err = tk.Exec("drop global " + testSQL.dropSQL) require.Equal(t, uint64(1), tk.Session().AffectedRows()) require.NoError(t, err) - _, fuzzyDigest = bindinfo.NormalizeStmtForFuzzyBinding(stmt) + _, fuzzyDigest = norm.NormalizeStmtForBinding(stmt, norm.WithFuzz(true)) bindData, err = dom.BindHandle().MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) require.NoError(t, err) require.Nil(t, bindData) @@ -503,7 +504,7 @@ func TestGlobalBinding(t *testing.T) { require.NoError(t, err) require.Equal(t, 0, bindHandle.Size()) - _, fuzzyDigest = bindinfo.NormalizeStmtForFuzzyBinding(stmt) + _, fuzzyDigest = norm.NormalizeStmtForBinding(stmt, norm.WithFuzz(true)) bindData, err = dom.BindHandle().MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) require.NoError(t, err) require.Nil(t, bindData) diff --git a/pkg/bindinfo/norm/BUILD.bazel b/pkg/bindinfo/norm/BUILD.bazel new file mode 100644 index 0000000000000..8cc0b53f8f159 --- /dev/null +++ b/pkg/bindinfo/norm/BUILD.bazel @@ -0,0 +1,13 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "norm", + srcs = ["normalize.go"], + importpath = "github.com/pingcap/tidb/pkg/bindinfo/norm", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/util/parser", + ], +) diff --git a/pkg/bindinfo/norm/normalize.go b/pkg/bindinfo/norm/normalize.go new file mode 100644 index 0000000000000..4772c35cb3032 --- /dev/null +++ b/pkg/bindinfo/norm/normalize.go @@ -0,0 +1,127 @@ +// 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 norm + +import ( + "strings" + + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + utilparser "github.com/pingcap/tidb/pkg/util/parser" +) + +type option struct { + specifiedDB string + fuzz bool +} + +type optionFunc func(*option) + +// WithFuzz specifies whether to eliminate schema names. +func WithFuzz(fuzz bool) optionFunc { + return func(user *option) { + user.fuzz = fuzz + } +} + +// WithSpecifiedDB specifies the specified DB name. +func WithSpecifiedDB(specifiedDB string) optionFunc { + return func(user *option) { + user.specifiedDB = specifiedDB + } +} + +// NormalizeStmtForBinding normalizes a statement for binding. +// when fuzz is false, schema names will be completed automatically: `select * from t` --> `select * from db . t`. +// when fuzz is true, schema names will be eliminated automatically: `select * from db . t` --> `select * from t`. +func NormalizeStmtForBinding(stmtNode ast.StmtNode, options ...optionFunc) (normalizedStmt, exactSQLDigest string) { + opt := &option{} + for _, option := range options { + option(opt) + } + return normalizeStmt(stmtNode, opt.specifiedDB, opt.fuzz) +} + +// NormalizeStmtForBinding normalizes a statement for binding. +// This function skips Explain automatically, and literals in in-lists will be normalized as '...'. +// For normal bindings, DB name will be completed automatically: +// +// e.g. `select * from t where a in (1, 2, 3)` --> `select * from test.t where a in (...)` +func normalizeStmt(stmtNode ast.StmtNode, specifiedDB string, fuzzy bool) (normalizedStmt, sqlDigest string) { + normalize := func(n ast.StmtNode) (normalizedStmt, sqlDigest string) { + eraseLastSemicolon(n) + var digest *parser.Digest + var normalizedSQL string + if !fuzzy { + normalizedSQL = utilparser.RestoreWithDefaultDB(n, specifiedDB, n.Text()) + } else { + normalizedSQL = utilparser.RestoreWithoutDB(n) + } + normalizedStmt, digest = parser.NormalizeDigestForBinding(normalizedSQL) + return normalizedStmt, digest.String() + } + + switch x := stmtNode.(type) { + case *ast.ExplainStmt: + // This function is only used to find bind record. + // For some SQLs, such as `explain select * from t`, they will be entered here many times, + // but some of them do not want to obtain bind record. + // The difference between them is whether len(x.Text()) is empty. They cannot be distinguished by stmt.restore. + // For these cases, we need return "" as normalize SQL and hash. + if len(x.Text()) == 0 { + return "", "" + } + switch x.Stmt.(type) { + case *ast.SelectStmt, *ast.DeleteStmt, *ast.UpdateStmt, *ast.InsertStmt: + normalizeSQL, digest := normalize(x.Stmt) + return normalizeSQL, digest + case *ast.SetOprStmt: + normalizeExplainSQL, _ := normalize(x) + + idx := strings.Index(normalizeExplainSQL, "select") + parenthesesIdx := strings.Index(normalizeExplainSQL, "(") + if parenthesesIdx != -1 && parenthesesIdx < idx { + idx = parenthesesIdx + } + // If the SQL is `EXPLAIN ((VALUES ROW ()) ORDER BY 1);`, the idx will be -1. + if idx == -1 { + hash := parser.DigestNormalized(normalizeExplainSQL) + return normalizeExplainSQL, hash.String() + } + normalizeSQL := normalizeExplainSQL[idx:] + hash := parser.DigestNormalized(normalizeSQL) + return normalizeSQL, hash.String() + } + case *ast.SelectStmt, *ast.SetOprStmt, *ast.DeleteStmt, *ast.UpdateStmt, *ast.InsertStmt: + // This function is only used to find bind record. + // For some SQLs, such as `explain select * from t`, they will be entered here many times, + // but some of them do not want to obtain bind record. + // The difference between them is whether len(x.Text()) is empty. They cannot be distinguished by stmt.restore. + // For these cases, we need return "" as normalize SQL and hash. + if len(x.Text()) == 0 { + return "", "" + } + normalizedSQL, digest := normalize(x) + return normalizedSQL, digest + } + return "", "" +} + +func eraseLastSemicolon(stmt ast.StmtNode) { + sql := stmt.Text() + if len(sql) > 0 && sql[len(sql)-1] == ';' { + stmt.SetText(nil, sql[:len(sql)-1]) + } +} diff --git a/pkg/bindinfo/session_handle.go b/pkg/bindinfo/session_handle.go index e2d9f2fcb8a68..3084b0fd5ad88 100644 --- a/pkg/bindinfo/session_handle.go +++ b/pkg/bindinfo/session_handle.go @@ -21,6 +21,7 @@ import ( "time" "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/bindinfo/norm" "github.com/pingcap/tidb/pkg/metrics" "github.com/pingcap/tidb/pkg/parser" "github.com/pingcap/tidb/pkg/parser/ast" @@ -117,7 +118,7 @@ func (h *sessionBindingHandle) MatchSessionBinding(sctx sessionctx.Context, fuzz if err != nil { return nil, err } - _, bindingFuzzyDigest := NormalizeStmtForFuzzyBinding(bindingStmt) + _, bindingFuzzyDigest := norm.NormalizeStmtForBinding(bindingStmt, norm.WithFuzz(true)) if bindingFuzzyDigest != fuzzyDigest { continue } diff --git a/pkg/bindinfo/session_handle_test.go b/pkg/bindinfo/session_handle_test.go index 09f1848206b48..765ada9a1bf51 100644 --- a/pkg/bindinfo/session_handle_test.go +++ b/pkg/bindinfo/session_handle_test.go @@ -22,6 +22,7 @@ import ( "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/metrics" "github.com/pingcap/tidb/pkg/parser" "github.com/pingcap/tidb/pkg/parser/auth" @@ -113,7 +114,7 @@ func TestSessionBinding(t *testing.T) { stmt, err := parser.New().ParseOneStmt(testSQL.originSQL, "", "") require.NoError(t, err) - _, fuzzyDigest := bindinfo.NormalizeStmtForFuzzyBinding(stmt) + _, fuzzyDigest := norm.NormalizeStmtForBinding(stmt, norm.WithFuzz(true)) bindData, err := handle.MatchSessionBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) require.NoError(t, err) require.NotNil(t, bindData) @@ -152,7 +153,7 @@ func TestSessionBinding(t *testing.T) { _, err = tk.Exec("drop session " + testSQL.dropSQL) require.NoError(t, err) - _, fuzzyDigest = bindinfo.NormalizeStmtForFuzzyBinding(stmt) + _, fuzzyDigest = norm.NormalizeStmtForBinding(stmt, norm.WithFuzz(true)) bindData, err = handle.MatchSessionBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) require.NoError(t, err) require.Nil(t, bindData) // dropped diff --git a/pkg/bindinfo/tests/BUILD.bazel b/pkg/bindinfo/tests/BUILD.bazel index 15b0c93b23998..91b4b02588659 100644 --- a/pkg/bindinfo/tests/BUILD.bazel +++ b/pkg/bindinfo/tests/BUILD.bazel @@ -13,6 +13,7 @@ go_test( deps = [ "//pkg/bindinfo", "//pkg/bindinfo/internal", + "//pkg/bindinfo/norm", "//pkg/domain", "//pkg/parser", "//pkg/parser/model", diff --git a/pkg/bindinfo/tests/bind_test.go b/pkg/bindinfo/tests/bind_test.go index 52f7f53bff7b1..2edc9e9192af1 100644 --- a/pkg/bindinfo/tests/bind_test.go +++ b/pkg/bindinfo/tests/bind_test.go @@ -22,6 +22,7 @@ import ( "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/domain" "github.com/pingcap/tidb/pkg/parser" "github.com/pingcap/tidb/pkg/parser/model" @@ -320,7 +321,7 @@ func TestBindingSymbolList(t *testing.T) { stmt, err := parser.New().ParseOneStmt("select a, b from test . t where a = 1 limit 0, 1", "", "") require.NoError(t, err) - _, fuzzyDigest := bindinfo.NormalizeStmtForFuzzyBinding(stmt) + _, fuzzyDigest := norm.NormalizeStmtForBinding(stmt, norm.WithFuzz(true)) bindData, err := dom.BindHandle().MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) require.NoError(t, err) require.NotNil(t, bindData) @@ -367,7 +368,7 @@ func TestBindingInListWithSingleLiteral(t *testing.T) { stmt, err := parser.New().ParseOneStmt("select a, b from test . t where a in (1)", "", "") require.NoError(t, err) - _, fuzzyDigest := bindinfo.NormalizeStmtForFuzzyBinding(stmt) + _, fuzzyDigest := norm.NormalizeStmtForBinding(stmt, norm.WithFuzz(true)) bindData, err := dom.BindHandle().MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) require.NoError(t, err) require.NotNil(t, bindData) @@ -405,7 +406,7 @@ func TestBestPlanInBaselines(t *testing.T) { stmt, _, _ := internal.UtilNormalizeWithDefaultDB(t, "select a, b from t where a = 1 limit 0, 1") - _, fuzzyDigest := bindinfo.NormalizeStmtForFuzzyBinding(stmt) + _, fuzzyDigest := norm.NormalizeStmtForBinding(stmt, norm.WithFuzz(true)) bindData, err := dom.BindHandle().MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) require.NoError(t, err) require.NotNil(t, bindData) @@ -441,7 +442,7 @@ func TestErrorBind(t *testing.T) { stmt, err := parser.New().ParseOneStmt("select * from test . t where i > ?", "", "") require.NoError(t, err) - _, fuzzyDigest := bindinfo.NormalizeStmtForFuzzyBinding(stmt) + _, fuzzyDigest := norm.NormalizeStmtForBinding(stmt, norm.WithFuzz(true)) bindData, err := dom.BindHandle().MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) require.NoError(t, err) require.NotNil(t, bindData) @@ -500,7 +501,7 @@ func TestHintsSetID(t *testing.T) { // Verify the added Binding contains ID with restored query block. stmt, err := parser.New().ParseOneStmt("select * from t where a > ?", "", "") require.NoError(t, err) - _, fuzzyDigest := bindinfo.NormalizeStmtForFuzzyBinding(stmt) + _, fuzzyDigest := norm.NormalizeStmtForBinding(stmt, norm.WithFuzz(true)) bindData, err := dom.BindHandle().MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) require.NoError(t, err) require.NotNil(t, bindData) @@ -511,7 +512,7 @@ func TestHintsSetID(t *testing.T) { internal.UtilCleanBindingEnv(tk, dom) tk.MustExec("create global binding for select * from t where a > 10 using select /*+ use_index(t, idx_a) */ * from t where a > 10") - _, fuzzyDigest = bindinfo.NormalizeStmtForFuzzyBinding(stmt) + _, fuzzyDigest = norm.NormalizeStmtForBinding(stmt, norm.WithFuzz(true)) bindData, err = dom.BindHandle().MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) require.NoError(t, err) require.NotNil(t, bindData) @@ -522,7 +523,7 @@ func TestHintsSetID(t *testing.T) { internal.UtilCleanBindingEnv(tk, dom) tk.MustExec("create global binding for select * from t where a > 10 using select /*+ use_index(@sel_1 t, idx_a) */ * from t where a > 10") - _, fuzzyDigest = bindinfo.NormalizeStmtForFuzzyBinding(stmt) + _, fuzzyDigest = norm.NormalizeStmtForBinding(stmt, norm.WithFuzz(true)) bindData, err = dom.BindHandle().MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) require.NoError(t, err) require.NotNil(t, bindData) @@ -533,7 +534,7 @@ func TestHintsSetID(t *testing.T) { internal.UtilCleanBindingEnv(tk, dom) tk.MustExec("create global binding for select * from t where a > 10 using select /*+ use_index(@qb1 t, idx_a) qb_name(qb1) */ * from t where a > 10") - _, fuzzyDigest = bindinfo.NormalizeStmtForFuzzyBinding(stmt) + _, fuzzyDigest = norm.NormalizeStmtForBinding(stmt, norm.WithFuzz(true)) bindData, err = dom.BindHandle().MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) require.NoError(t, err) require.NotNil(t, bindData) @@ -544,7 +545,7 @@ func TestHintsSetID(t *testing.T) { internal.UtilCleanBindingEnv(tk, dom) tk.MustExec("create global binding for select * from t where a > 10 using select /*+ use_index(T, IDX_A) */ * from t where a > 10") - _, fuzzyDigest = bindinfo.NormalizeStmtForFuzzyBinding(stmt) + _, fuzzyDigest = norm.NormalizeStmtForBinding(stmt, norm.WithFuzz(true)) bindData, err = dom.BindHandle().MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) require.NoError(t, err) require.NotNil(t, bindData) @@ -557,7 +558,7 @@ func TestHintsSetID(t *testing.T) { err = tk.ExecToErr("create global binding for select * from t using select /*+ non_exist_hint() */ * from t") require.True(t, terror.ErrorEqual(err, parser.ErrParse)) tk.MustExec("create global binding for select * from t where a > 10 using select * from t where a > 10") - _, fuzzyDigest = bindinfo.NormalizeStmtForFuzzyBinding(stmt) + _, fuzzyDigest = norm.NormalizeStmtForBinding(stmt, norm.WithFuzz(true)) bindData, err = dom.BindHandle().MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) require.NoError(t, err) require.NotNil(t, bindData) diff --git a/pkg/planner/core/BUILD.bazel b/pkg/planner/core/BUILD.bazel index dd0f476093ed2..599317b5d7366 100644 --- a/pkg/planner/core/BUILD.bazel +++ b/pkg/planner/core/BUILD.bazel @@ -87,6 +87,7 @@ go_library( deps = [ "//br/pkg/storage", "//pkg/bindinfo", + "//pkg/bindinfo/norm", "//pkg/config", "//pkg/distsql", "//pkg/domain", diff --git a/pkg/planner/core/planbuilder.go b/pkg/planner/core/planbuilder.go index aa95774ff12b8..1548cdd9caa8f 100644 --- a/pkg/planner/core/planbuilder.go +++ b/pkg/planner/core/planbuilder.go @@ -27,6 +27,7 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/tidb/br/pkg/storage" "github.com/pingcap/tidb/pkg/bindinfo" + "github.com/pingcap/tidb/pkg/bindinfo/norm" "github.com/pingcap/tidb/pkg/config" "github.com/pingcap/tidb/pkg/domain" "github.com/pingcap/tidb/pkg/expression" @@ -734,7 +735,7 @@ func (b *PlanBuilder) buildSet(ctx context.Context, v *ast.SetStmt) (Plan, error func (b *PlanBuilder) buildDropBindPlan(v *ast.DropBindingStmt) (Plan, error) { var p *SQLBindPlan if v.OriginNode != nil { - normdOrigSQL, sqlDigestWithDB := bindinfo.NormalizeStmtForBinding(v.OriginNode, b.ctx.GetSessionVars().CurrentDB) + normdOrigSQL, sqlDigestWithDB := norm.NormalizeStmtForBinding(v.OriginNode, norm.WithSpecifiedDB(b.ctx.GetSessionVars().CurrentDB)) p = &SQLBindPlan{ SQLBindOp: OpSQLBindDrop, NormdOrigSQL: normdOrigSQL,