Skip to content

Commit 2347e97

Browse files
authored
planner: add an unique key into mysql.bind_info (#60296)
ref #60148
1 parent 84183c5 commit 2347e97

File tree

6 files changed

+116
-173
lines changed

6 files changed

+116
-173
lines changed

br/pkg/restore/snap_client/systable_restore_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,5 +117,5 @@ func TestCheckSysTableCompatibility(t *testing.T) {
117117

118118
// The above variables are in the file br/pkg/restore/systable_restore.go
119119
func TestMonitorTheSystemTableIncremental(t *testing.T) {
120-
require.Equal(t, int64(245), session.CurrentBootstrapVersion)
120+
require.Equal(t, int64(246), session.CurrentBootstrapVersion)
121121
}

pkg/bindinfo/binding_operator.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@ func (op *bindingOperator) CreateBinding(sctx sessionctx.Context, bindings []*Bi
9797
// overflow directly.
9898

9999
// Insert the Bindings to the storage.
100+
var sqlDigest, planDigest any // null by default
101+
if binding.SQLDigest != "" {
102+
sqlDigest = binding.SQLDigest
103+
}
104+
if binding.PlanDigest != "" {
105+
planDigest = binding.PlanDigest
106+
}
100107
_, err = exec(
101108
sctx,
102109
`INSERT INTO mysql.bind_info VALUES (%?,%?, %?, %?, %?, %?, %?, %?, %?, %?, %?)`,
@@ -109,8 +116,8 @@ func (op *bindingOperator) CreateBinding(sctx sessionctx.Context, bindings []*Bi
109116
binding.Charset,
110117
binding.Collation,
111118
binding.Source,
112-
binding.SQLDigest,
113-
binding.PlanDigest,
119+
sqlDigest,
120+
planDigest,
114121
)
115122
failpoint.Inject("CreateGlobalBindingNthFail", func(val failpoint.Value) {
116123
n := val.(int)

pkg/bindinfo/binding_operator_test.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -260,8 +260,6 @@ func TestSetBindingStatusWithoutBindingInCache(t *testing.T) {
260260

261261
// Simulate creating bindings on other machines
262262
_, sqlDigest := parser.NormalizeDigestForBinding("select * from `test` . `t` where `a` > ?")
263-
tk.MustExec("insert into mysql.bind_info values('select * from `test` . `t` where `a` > ?', 'SELECT /*+ USE_INDEX(`t` `idx_a`)*/ * FROM `test`.`t` WHERE `a` > 10', 'test', 'deleted', '2000-01-01 09:00:00', '2000-01-01 09:00:00', '', '','" +
264-
bindinfo.SourceManual + "', '" + sqlDigest.String() + "', '')")
265263
tk.MustExec("insert into mysql.bind_info values('select * from `test` . `t` where `a` > ?', 'SELECT /*+ USE_INDEX(`t` `idx_a`)*/ * FROM `test`.`t` WHERE `a` > 10', 'test', 'enabled', '2000-01-02 09:00:00', '2000-01-02 09:00:00', '', '','" +
266264
bindinfo.SourceManual + "', '" + sqlDigest.String() + "', '')")
267265
tk.MustExec("set binding disabled for select * from t where a > 10")
@@ -274,8 +272,6 @@ func TestSetBindingStatusWithoutBindingInCache(t *testing.T) {
274272
utilCleanBindingEnv(tk)
275273

276274
// Simulate creating bindings on other machines
277-
tk.MustExec("insert into mysql.bind_info values('select * from `test` . `t` where `a` > ?', 'SELECT * FROM `test`.`t` WHERE `a` > 10', 'test', 'deleted', '2000-01-01 09:00:00', '2000-01-01 09:00:00', '', '','" +
278-
bindinfo.SourceManual + "', '" + sqlDigest.String() + "', '')")
279275
tk.MustExec("insert into mysql.bind_info values('select * from `test` . `t` where `a` > ?', 'SELECT * FROM `test`.`t` WHERE `a` > 10', 'test', 'disabled', '2000-01-02 09:00:00', '2000-01-02 09:00:00', '', '','" +
280276
bindinfo.SourceManual + "', '" + sqlDigest.String() + "', '')")
281277
tk.MustExec("set binding enabled for select * from t where a > 10")

pkg/session/BUILD.bazel

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,6 @@ go_test(
155155
shard_count = 50,
156156
deps = [
157157
"//pkg/autoid_service",
158-
"//pkg/bindinfo",
159158
"//pkg/config",
160159
"//pkg/ddl",
161160
"//pkg/domain",
@@ -165,7 +164,6 @@ go_test(
165164
"//pkg/keyspace",
166165
"//pkg/kv",
167166
"//pkg/meta",
168-
"//pkg/parser",
169167
"//pkg/parser/ast",
170168
"//pkg/parser/auth",
171169
"//pkg/session/types",

pkg/session/bootstrap.go

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -304,10 +304,11 @@ const (
304304
charset TEXT NOT NULL,
305305
collation TEXT NOT NULL,
306306
source VARCHAR(10) NOT NULL DEFAULT 'unknown',
307-
sql_digest varchar(64),
308-
plan_digest varchar(64),
307+
sql_digest varchar(64) DEFAULT NULL,
308+
plan_digest varchar(64) DEFAULT NULL,
309309
INDEX sql_index(original_sql(700),default_db(68)) COMMENT "accelerate the speed when add global binding query",
310-
INDEX time_index(update_time) COMMENT "accelerate the speed when querying with last update time"
310+
INDEX time_index(update_time) COMMENT "accelerate the speed when querying with last update time",
311+
UNIQUE INDEX digest_index(plan_digest, sql_digest) COMMENT "avoid duplicated records"
311312
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;`
312313

313314
// CreateRoleEdgesTable stores the role and user relationship information.
@@ -1275,11 +1276,14 @@ const (
12751276

12761277
// version245 updates column types of mysql.bind_info.
12771278
version245 = 245
1279+
1280+
// version246 adds new unique index for mysql.bind_info.
1281+
version246 = 246
12781282
)
12791283

12801284
// currentBootstrapVersion is defined as a variable, so we can modify its value for testing.
12811285
// please make sure this is the largest version
1282-
var currentBootstrapVersion int64 = version245
1286+
var currentBootstrapVersion int64 = version246
12831287

12841288
// DDL owner key's expired time is ManagerSessionTTL seconds, we should wait the time and give more time to have a chance to finish it.
12851289
var internalSQLTimeout = owner.ManagerSessionTTL + 15
@@ -1460,6 +1464,7 @@ var (
14601464
upgradeToVer243,
14611465
upgradeToVer244,
14621466
upgradeToVer245,
1467+
upgradeToVer246,
14631468
}
14641469
)
14651470

@@ -3406,6 +3411,60 @@ func upgradeToVer245(s sessiontypes.Session, ver int64) {
34063411
doReentrantDDL(s, "ALTER TABLE mysql.bind_info MODIFY COLUMN bind_sql LONGTEXT NOT NULL")
34073412
}
34083413

3414+
func upgradeToVer246(s sessiontypes.Session, ver int64) {
3415+
if ver >= version246 {
3416+
return
3417+
}
3418+
3419+
// log duplicated digests that will be set to null.
3420+
ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap)
3421+
rs, err := s.ExecuteInternal(ctx,
3422+
`select plan_digest, sql_digest from mysql.bind_info group by plan_digest, sql_digest having count(1) > 1`)
3423+
if err != nil {
3424+
logutil.BgLogger().Fatal("failed to get duplicated plan and sql digests", zap.Error(err))
3425+
return
3426+
}
3427+
req := rs.NewChunk(nil)
3428+
duplicatedDigests := make(map[string]struct{})
3429+
for {
3430+
err = rs.Next(ctx, req)
3431+
if err != nil {
3432+
logutil.BgLogger().Fatal("failed to get duplicated plan and sql digests", zap.Error(err))
3433+
return
3434+
}
3435+
if req.NumRows() == 0 {
3436+
break
3437+
}
3438+
for i := 0; i < req.NumRows(); i++ {
3439+
planDigest, sqlDigest := req.GetRow(i).GetString(0), req.GetRow(i).GetString(1)
3440+
duplicatedDigests[sqlDigest+", "+planDigest] = struct{}{}
3441+
}
3442+
req.Reset()
3443+
}
3444+
if err := rs.Close(); err != nil {
3445+
logutil.BgLogger().Warn("failed to close record set", zap.Error(err))
3446+
}
3447+
if len(duplicatedDigests) > 0 {
3448+
digestList := make([]string, 0, len(duplicatedDigests))
3449+
for k := range duplicatedDigests {
3450+
digestList = append(digestList, "("+k+")")
3451+
}
3452+
logutil.BgLogger().Warn("set the following (plan digest, sql digest) in mysql.bind_info to null " +
3453+
"for adding new unique index: " + strings.Join(digestList, ", "))
3454+
}
3455+
3456+
// to avoid the failure of adding the unique index, remove duplicated rows on these 2 digest columns first.
3457+
// in most cases, there should be no duplicated rows, since now we only store one binding for each sql_digest.
3458+
// compared with upgrading failure, it's OK to set these 2 columns to null.
3459+
doReentrantDDL(s, `UPDATE mysql.bind_info SET plan_digest=null, sql_digest=null
3460+
WHERE (plan_digest, sql_digest) in (
3461+
select plan_digest, sql_digest from mysql.bind_info
3462+
group by plan_digest, sql_digest having count(1) > 1)`)
3463+
doReentrantDDL(s, "ALTER TABLE mysql.bind_info MODIFY COLUMN sql_digest VARCHAR(64) DEFAULT NULL")
3464+
doReentrantDDL(s, "ALTER TABLE mysql.bind_info MODIFY COLUMN plan_digest VARCHAR(64) DEFAULT NULL")
3465+
doReentrantDDL(s, "ALTER TABLE mysql.bind_info ADD UNIQUE INDEX digest_index(plan_digest, sql_digest)", dbterror.ErrDupKeyName)
3466+
}
3467+
34093468
// initGlobalVariableIfNotExists initialize a global variable with specific val if it does not exist.
34103469
func initGlobalVariableIfNotExists(s sessiontypes.Session, name string, val any) {
34113470
ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap)

0 commit comments

Comments
 (0)