Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bindinfo: support disabled status for binding #32819

Merged
merged 49 commits into from
Mar 16, 2022
Merged
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
113f3b4
bindinfo: rename Using status to Enable status
Reminiscent Mar 3, 2022
65da03b
convert the Using status binding to Enable status binding
Reminiscent Mar 3, 2022
c16dbf7
Merge branch 'master' of github.com:pingcap/tidb into bindingStatus
Reminiscent Mar 3, 2022
5ac35cd
address comments and fix ut
Reminiscent Mar 4, 2022
2a4f21e
Merge branch 'master' of github.com:pingcap/tidb into bindingStatus
Reminiscent Mar 4, 2022
7a49f34
fix ut
Reminiscent Mar 4, 2022
9fc6cff
bindinfo: support disable status for binding
Reminiscent Mar 4, 2022
52e91bf
fixup
Reminiscent Mar 4, 2022
7dff9d8
rename enable and disable to enabled and disabled
Reminiscent Mar 4, 2022
1b3a47e
Merge branch 'master' of github.com:pingcap/tidb into bindingStatus
Reminiscent Mar 4, 2022
722045d
remove useless change
Reminiscent Mar 4, 2022
dfbe5c6
change the parser part
Reminiscent Mar 4, 2022
adaf1cd
remove useless change
Reminiscent Mar 4, 2022
053bcf2
Merge branch 'master' of github.com:pingcap/tidb into bindingStatus
Reminiscent Mar 4, 2022
1e5008d
fix ut
Reminiscent Mar 4, 2022
3e8adb7
Merge branch 'master' of github.com:pingcap/tidb into bindingStatus
Reminiscent Mar 4, 2022
0a0fa6d
Merge branch 'master' into bindingStatus
ti-chi-bot Mar 7, 2022
164bcc1
Merge branch 'master' into bindingStatus
ti-chi-bot Mar 7, 2022
feb255c
Merge branches 'bindingStatus' and 'supportDisableStatus' of github.c…
Reminiscent Mar 8, 2022
a4b024b
Merge branch 'master' of github.com:pingcap/tidb into supportDisableS…
Reminiscent Mar 8, 2022
df39a8f
rename
Reminiscent Mar 8, 2022
b6b2b11
fix typo
Reminiscent Mar 8, 2022
4134d63
remove useless change
Reminiscent Mar 8, 2022
a1e68c8
update the status from using to enabled when upgrade
Reminiscent Mar 8, 2022
479d86e
Merge branch 'master' into supportDisableStatus
qw4990 Mar 8, 2022
a514afb
Merge branch 'master' into supportDisableStatus
qw4990 Mar 8, 2022
8c45877
add more comments
Reminiscent Mar 9, 2022
6f6a355
Merge branch 'master' into supportDisableStatus
qw4990 Mar 9, 2022
6476661
planner: support 'set binding enabled/ disabled'
Reminiscent Mar 9, 2022
62aecaf
Merge branch 'master' of github.com:pingcap/tidb into supportDisableS…
Reminiscent Mar 9, 2022
f58b8d1
Merge remote-tracking branch 'origin/supportDisableStatus' into suppo…
Reminiscent Mar 9, 2022
04069bf
format the header
Reminiscent Mar 9, 2022
d199115
fix check_dev
Reminiscent Mar 9, 2022
31f315c
Merge branch 'master' into supportDisableStatus
Reminiscent Mar 10, 2022
ae5dc5b
add more tests
Reminiscent Mar 15, 2022
fb12213
Merge branch 'master' of github.com:pingcap/tidb into supportDisableS…
Reminiscent Mar 15, 2022
d80d763
Merge remote-tracking branch 'origin/supportDisableStatus' into suppo…
Reminiscent Mar 15, 2022
7c96a00
Merge branch 'master' of github.com:pingcap/tidb into supportDisableS…
Reminiscent Mar 15, 2022
27272c9
update the warning message
Reminiscent Mar 15, 2022
b601865
add more test cases
Reminiscent Mar 15, 2022
f5b59a4
remove useless change
Reminiscent Mar 15, 2022
0c11684
Merge branch 'master' into supportDisableStatus
Reminiscent Mar 15, 2022
b196859
add the test for Privilege
Reminiscent Mar 16, 2022
ecf4d52
Merge branch 'master' of github.com:pingcap/tidb into supportDisableS…
Reminiscent Mar 16, 2022
fe9e03e
Merge remote-tracking branch 'origin/supportDisableStatus' into suppo…
Reminiscent Mar 16, 2022
f09ec70
address comments
Reminiscent Mar 16, 2022
f7934c2
Merge branch 'master' of github.com:pingcap/tidb into supportDisableS…
Reminiscent Mar 16, 2022
c8f0977
address comments
Reminiscent Mar 16, 2022
e293107
Merge branch 'master' into supportDisableStatus
ti-chi-bot Mar 16, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions bindinfo/bind_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1038,6 +1038,10 @@ func TestSPMHitInfo(t *testing.T) {
require.True(t, tk.HasPlan("SELECT * from t1,t2 where t1.id = t2.id", "MergeJoin"))
tk.MustExec("SELECT * from t1,t2 where t1.id = t2.id")
tk.MustQuery(`select @@last_plan_from_binding;`).Check(testkit.Rows("1"))
tk.MustExec("set binding disabled for SELECT * from t1,t2 where t1.id = t2.id")
tk.MustExec("SELECT * from t1,t2 where t1.id = t2.id")
tk.MustQuery(`select @@last_plan_from_binding;`).Check(testkit.Rows("0"))

tk.MustExec("drop global binding for SELECT * from t1,t2 where t1.id = t2.id")
}

Expand Down Expand Up @@ -1165,6 +1169,9 @@ func TestSPMWithoutUseDatabase(t *testing.T) {
require.True(t, tk1.MustUseIndex("select * from test.t", "a"))
tk1.MustExec("select * from test.t")
tk1.MustQuery(`select @@last_plan_from_binding;`).Check(testkit.Rows("1"))
tk1.MustExec("set binding disabled for select * from test.t")
tk1.MustExec("select * from test.t")
tk1.MustQuery(`select @@last_plan_from_binding;`).Check(testkit.Rows("0"))
}

func TestBindingWithoutCharset(t *testing.T) {
Expand Down
60 changes: 60 additions & 0 deletions bindinfo/capture_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,66 @@ func TestCapturePlanBaseline(t *testing.T) {
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, clean := testkit.CreateMockStoreAndDomain(t)
defer clean()

tk := testkit.NewTestKit(t, store)

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.True(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil))
tk.MustExec("select * from t where a > 10")
tk.MustExec("select * from t where a > 10")
tk.MustExec("admin capture bindings")
rows = tk.MustQuery("show global bindings").Rows()
require.Len(t, rows, 1)
require.Equal(t, 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)

utilCleanBindingEnv(tk, dom)
}

func TestCaptureDBCaseSensitivity(t *testing.T) {
store, clean := testkit.CreateMockStore(t)
defer clean()
Expand Down
86 changes: 86 additions & 0 deletions bindinfo/handle.go
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,92 @@ func (h *BindHandle) DropBindRecord(originalSQL, db string, binding *Binding) (e
return err
}

// SetBindRecordStatus set a BindRecord's status to the storage and bind cache.
func (h *BindHandle) SetBindRecordStatus(originalSQL string, binding *Binding, newStatus string) (ok bool, err error) {
h.bindInfo.Lock()
h.sctx.Lock()
defer func() {
h.sctx.Unlock()
h.bindInfo.Unlock()
}()
exec, _ := h.sctx.Context.(sqlexec.SQLExecutor)
_, err = exec.ExecuteInternal(context.TODO(), "BEGIN PESSIMISTIC")
if err != nil {
return
}
var (
updateTs types.Time
oldStatus0, oldStatus1 string
affectRows int
)
if newStatus == Disabled {
// For compatibility reasons, when we need to 'set binding disabled for <stmt>',
// we need to consider both the 'enabled' and 'using' status.
oldStatus0 = Using
oldStatus1 = Enabled
} else if newStatus == Enabled {
// In order to unify the code, two identical old statuses are set.
oldStatus0 = Disabled
oldStatus1 = Disabled
}
defer func() {
if err != nil {
_, err1 := exec.ExecuteInternal(context.TODO(), "ROLLBACK")
terror.Log(err1)
return
}

_, err = exec.ExecuteInternal(context.TODO(), "COMMIT")
if err != nil {
return
}
if affectRows == 0 {
return
}

// The set binding status operation is success.
ok = true
record := &BindRecord{OriginalSQL: originalSQL}
sqlDigest := parser.DigestNormalized(record.OriginalSQL)
oldRecord := h.GetBindRecord(sqlDigest.String(), originalSQL, "")
setBindingStatusInCacheSucc := false
if oldRecord != nil && len(oldRecord.Bindings) > 0 {
record.Bindings = make([]Binding, len(oldRecord.Bindings))
copy(record.Bindings, oldRecord.Bindings)
for ind, oldBinding := range record.Bindings {
if oldBinding.Status == oldStatus0 || oldBinding.Status == oldStatus1 {
if binding == nil || (binding != nil && oldBinding.isSame(binding)) {
setBindingStatusInCacheSucc = true
record.Bindings[ind].Status = newStatus
record.Bindings[ind].UpdateTime = updateTs
}
}
}
}
if setBindingStatusInCacheSucc {
h.setBindRecord(sqlDigest.String(), record)
}
}()

// Lock mysql.bind_info to synchronize with SetBindingStatus on other tidb instances.
if err = h.lockBindInfoTable(); err != nil {
return
}

updateTs = types.NewTime(types.FromGoTime(time.Now()), mysql.TypeTimestamp, 3)
updateTsStr := updateTs.String()

if binding == nil {
_, err = exec.ExecuteInternal(context.TODO(), `UPDATE mysql.bind_info SET status = %?, update_time = %? WHERE original_sql = %? AND update_time < %? AND status IN (%?, %?)`,
newStatus, updateTsStr, originalSQL, updateTsStr, oldStatus0, oldStatus1)
} else {
_, err = exec.ExecuteInternal(context.TODO(), `UPDATE mysql.bind_info SET status = %?, update_time = %? WHERE original_sql = %? AND update_time < %? AND bind_sql = %? AND status IN (%?, %?)`,
newStatus, updateTsStr, originalSQL, updateTsStr, binding.BindSQL, oldStatus0, oldStatus1)
}
affectRows = int(h.sctx.Context.GetSessionVars().StmtCtx.AffectedRows())
return
}

// GCBindRecord physically removes the deleted bind records in mysql.bind_info.
func (h *BindHandle) GCBindRecord() (err error) {
h.bindInfo.Lock()
Expand Down
105 changes: 105 additions & 0 deletions bindinfo/handle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,111 @@ func TestEvolveInvalidBindings(t *testing.T) {
require.True(t, status == bindinfo.Enabled || status == bindinfo.Rejected)
}

func TestSetBindingStatus(t *testing.T) {
store, _, clean := testkit.CreateMockStoreAndDomain(t)
defer clean()

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))")
tk.MustQuery("show global bindings").Check(testkit.Rows())
tk.MustExec("create global binding for select * from t where a > 10 using select /*+ USE_INDEX(t, idx_a) */ * from t where a > 10")
rows := tk.MustQuery("show global bindings").Rows()
require.Len(t, rows, 1)
require.Equal(t, bindinfo.Enabled, rows[0][3])
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 using select * from t where a > 10")
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 There are no bindings can be set the status. Please check the SQL text"))
rows = tk.MustQuery("show global bindings").Rows()
require.Len(t, rows, 1)
require.Equal(t, bindinfo.Enabled, rows[0][3])
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")
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("set binding enabled for select * from t where a > 10")
rows = tk.MustQuery("show global bindings").Rows()
require.Len(t, rows, 1)
require.Equal(t, bindinfo.Enabled, rows[0][3])

tk.MustExec("set binding disabled for select * from t where a > 10")
tk.MustExec("create global binding for select * from t where a > 10 using select * from t where a > 10")
rows = tk.MustQuery("show global bindings").Rows()
require.Len(t, rows, 1)
require.Equal(t, bindinfo.Enabled, rows[0][3])
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 using select * from t where a > 10")
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("set binding enabled for select * from t where a > 10 using select * from t where a > 10")
rows = tk.MustQuery("show global bindings").Rows()
require.Len(t, rows, 1)
require.Equal(t, bindinfo.Enabled, rows[0][3])

tk.MustExec("set binding disabled for select * from t where a > 10 using select * from t where a > 10")
tk.MustExec("drop global binding for select * from t where a > 10 using select * from t where a > 10")
rows = tk.MustQuery("show global bindings").Rows()
require.Len(t, rows, 0)
}

func TestSetBindingStatusWithoutBindingInCache(t *testing.T) {
store, dom, clean := testkit.CreateMockStoreAndDomain(t)
defer clean()

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))")
utilCleanBindingEnv(tk, dom)
tk.MustQuery("show global bindings").Check(testkit.Rows())

// Simulate creating bindings on other machines
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', '', '','" +
bindinfo.Manual + "')")
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', '', '','" +
bindinfo.Manual + "')")
dom.BindHandle().Clear()
tk.MustExec("set binding disabled for select * from t where a > 10")
tk.MustExec("admin reload bindings")
rows := tk.MustQuery("show global bindings").Rows()
require.Len(t, rows, 1)
require.Equal(t, bindinfo.Disabled, rows[0][3])

// clear the mysql.bind_info
utilCleanBindingEnv(tk, dom)

// Simulate creating bindings on other machines
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', '', '','" +
bindinfo.Manual + "')")
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', '', '','" +
bindinfo.Manual + "')")
dom.BindHandle().Clear()
tk.MustExec("set binding enabled for select * from t where a > 10")
tk.MustExec("admin reload bindings")
rows = tk.MustQuery("show global bindings").Rows()
require.Len(t, rows, 1)
require.Equal(t, bindinfo.Enabled, rows[0][3])

utilCleanBindingEnv(tk, dom)
}

var testSQLs = []struct {
createSQL string
overlaySQL string
Expand Down
20 changes: 20 additions & 0 deletions executor/bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type SQLBindExec struct {
db string
isGlobal bool
bindAst ast.StmtNode
newStatus string
}

// Next implements the Executor Next interface.
Expand All @@ -55,6 +56,8 @@ func (e *SQLBindExec) Next(ctx context.Context, req *chunk.Chunk) error {
return e.evolveBindings()
case plannercore.OpReloadBindings:
return e.reloadBindings()
case plannercore.OpSetBindingStatus:
return e.setBindingStatus()
default:
return errors.Errorf("unsupported SQL bind operation: %v", e.sqlBindOp)
}
Expand All @@ -77,6 +80,23 @@ func (e *SQLBindExec) dropSQLBind() error {
return domain.GetDomain(e.ctx).BindHandle().DropBindRecord(e.normdOrigSQL, e.db, bindInfo)
}

func (e *SQLBindExec) setBindingStatus() error {
var bindInfo *bindinfo.Binding
if e.bindSQL != "" {
bindInfo = &bindinfo.Binding{
BindSQL: e.bindSQL,
Charset: e.charset,
Collation: e.collation,
}
}
ok, err := domain.GetDomain(e.ctx).BindHandle().SetBindRecordStatus(e.normdOrigSQL, bindInfo, e.newStatus)
if err == nil && !ok {
warningMess := errors.New("There are no bindings can be set the status. Please check the SQL text")
e.ctx.GetSessionVars().StmtCtx.AppendWarning(warningMess)
}
return err
}

func (e *SQLBindExec) createSQLBind() error {
// For audit log, SQLBindExec execute "explain" statement internally, save and recover stmtctx
// is necessary to avoid 'create binding' been recorded as 'explain'.
Expand Down
1 change: 1 addition & 0 deletions executor/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -4501,6 +4501,7 @@ func (b *executorBuilder) buildSQLBindExec(v *plannercore.SQLBindPlan) Executor
db: v.Db,
isGlobal: v.IsGlobal,
bindAst: v.BindStmt,
newStatus: v.NewStatus,
}
return e
}
Expand Down
Loading