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

planner: support drop binding by sql digest #39399

Merged
merged 23 commits into from
Nov 30, 2022
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
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
17 changes: 17 additions & 0 deletions bindinfo/bind_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,23 @@ func (c *bindCache) GetBindRecord(hash, normdOrigSQL, db string) *BindRecord {
return nil
}

// GetBindRecordBySQLDigest gets the BindRecord from the cache.
// The return value is not read-only, but it shouldn't be changed in the caller functions.
// The function is thread-safe.
func (c *bindCache) GetBindRecordBySQLDigest(hash string) (*BindRecord, error) {
qw4990 marked this conversation as resolved.
Show resolved Hide resolved
c.lock.Lock()
defer c.lock.Unlock()
bindings := c.get(bindCacheKey(hash))
if len(bindings) > 1 {
// currently, we only allow one binding for a sql
return nil, errors.New("more than 1 binding matched")
}
if len(bindings) == 0 || len(bindings[0].Bindings) == 0 {
return nil, errors.New("can't find any binding for `" + hash + "`")
}
return bindings[0], nil
}

// GetAllBindRecords return all the bindRecords from the bindCache.
// The return value is not read-only, but it shouldn't be changed in the caller functions.
// The function is thread-safe.
Expand Down
80 changes: 80 additions & 0 deletions bindinfo/bind_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1304,3 +1304,83 @@ func TestBindSQLDigest(t *testing.T) {
require.Equal(t, res[0][9], sqlDigestWithDB.String())
}
}

func TestDropBindBySQLDigest(t *testing.T) {
qw4990 marked this conversation as resolved.
Show resolved Hide resolved
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 int primary key, a int, b int, key(a), key(b))")

cases := []struct {
origin string
hint string
}{
// agg hints
{"select count(1) from t", "select /*+ hash_agg() */ count(1) from t"},
{"select count(1) from t", "select /*+ stream_agg() */ count(1) from t"},
// join hints
{"select * from t t1, t t2 where t1.a=t2.a", "select /*+ merge_join(t1, t2) */ * from t t1, t t2 where t1.a=t2.a"},
{"select * from t t1, t t2 where t1.a=t2.a", "select /*+ tidb_smj(t1, t2) */ * from t t1, t t2 where t1.a=t2.a"},
{"select * from t t1, t t2 where t1.a=t2.a", "select /*+ hash_join(t1, t2) */ * from t t1, t t2 where t1.a=t2.a"},
{"select * from t t1, t t2 where t1.a=t2.a", "select /*+ tidb_hj(t1, t2) */ * from t t1, t t2 where t1.a=t2.a"},
{"select * from t t1, t t2 where t1.a=t2.a", "select /*+ inl_join(t1, t2) */ * from t t1, t t2 where t1.a=t2.a"},
{"select * from t t1, t t2 where t1.a=t2.a", "select /*+ tidb_inlj(t1, t2) */ * from t t1, t t2 where t1.a=t2.a"},
{"select * from t t1, t t2 where t1.a=t2.a", "select /*+ inl_hash_join(t1, t2) */ * from t t1, t t2 where t1.a=t2.a"},
// index hints
{"select * from t", "select * from t use index(primary)"},
{"select * from t", "select /*+ use_index(primary) */ * from t"},
{"select * from t", "select * from t use index(a)"},
{"select * from t", "select /*+ use_index(a) */ * from t use index(a)"},
{"select * from t", "select * from t use index(b)"},
{"select * from t", "select /*+ use_index(b) */ * from t use index(b)"},
{"select a, b from t where a=1 or b=1", "select /*+ use_index_merge(t, a, b) */ a, b from t where a=1 or b=1"},
{"select * from t where a=1", "select /*+ ignore_index(t, a) */ * from t where a=1"},
// push-down hints
{"select * from t limit 10", "select /*+ limit_to_cop() */ * from t limit 10"},
{"select a, count(*) from t group by a", "select /*+ agg_to_cop() */ a, count(*) from t group by a"},
// index-merge hints
{"select a, b from t where a>1 or b>1", "select /*+ no_index_merge() */ a, b from t where a>1 or b>1"},
{"select a, b from t where a>1 or b>1", "select /*+ use_index_merge(t, a, b) */ a, b from t where a>1 or b>1"},
// runtime hints
{"select * from t", "select /*+ memory_quota(1024 MB) */ * from t"},
{"select * from t", "select /*+ max_execution_time(1000) */ * from t"},
// storage hints
{"select * from t", "select /*+ read_from_storage(tikv[t]) */ * from t"},
// others
{"select t1.a, t1.b from t t1 where t1.a in (select t2.a from t t2)", "select /*+ use_toja(true) */ t1.a, t1.b from t t1 where t1.a in (select t2.a from t t2)"},
}

h := dom.BindHandle()
// global scope
for _, c := range cases {
utilCleanBindingEnv(tk, dom)
sql := "create global binding for " + c.origin + " using " + c.hint
fzzf678 marked this conversation as resolved.
Show resolved Hide resolved
tk.MustExec(sql)
h.ReloadBindings()
res := tk.MustQuery(`show global bindings`).Rows()

require.Equal(t, len(res), 1)
require.Equal(t, len(res[0]), 11)
drop := fmt.Sprintf("drop global binding for sql digest '%s'", res[0][9])
tk.MustExec(drop)
require.NoError(t, h.GCBindRecord())
h.ReloadBindings()
tk.MustQuery("show global bindings").Check(testkit.Rows())
}

// session scope
for _, c := range cases {
utilCleanBindingEnv(tk, dom)
sql := "create binding for " + c.origin + " using " + c.hint
tk.MustExec(sql)
res := tk.MustQuery(`show bindings`).Rows()

require.Equal(t, len(res), 1)
require.Equal(t, len(res[0]), 11)
drop := fmt.Sprintf("drop binding for sql digest '%s'", res[0][9])
tk.MustExec(drop)
require.NoError(t, h.GCBindRecord())
tk.MustQuery("show bindings").Check(testkit.Rows())
}
}
14 changes: 14 additions & 0 deletions bindinfo/handle.go
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,15 @@ func (h *BindHandle) DropBindRecord(originalSQL, db string, binding *Binding) (d
return h.sctx.Context.GetSessionVars().StmtCtx.AffectedRows(), nil
}

// DropBindRecordByDigest drop BindRecord to the storage and BindRecord int the cache.
func (h *BindHandle) DropBindRecordByDigest(sqlDigest string) (deletedRows uint64, err error) {
oldRecord, err := h.GetBindRecordBySQLDigest(sqlDigest)
if err != nil {
return 0, err
}
return h.DropBindRecord(oldRecord.OriginalSQL, strings.ToLower(oldRecord.Db), nil)
}

// 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()
Expand Down Expand Up @@ -658,6 +667,11 @@ func (h *BindHandle) GetBindRecord(hash, normdOrigSQL, db string) *BindRecord {
return h.bindInfo.Load().(*bindCache).GetBindRecord(hash, normdOrigSQL, db)
}

// GetBindRecordBySQLDigest returns the BindRecord of the sql digest.
func (h *BindHandle) GetBindRecordBySQLDigest(hash string) (*BindRecord, error) {
qw4990 marked this conversation as resolved.
Show resolved Hide resolved
return h.bindInfo.Load().(*bindCache).GetBindRecordBySQLDigest(hash)
}

// GetAllBindRecord returns all bind records in cache.
func (h *BindHandle) GetAllBindRecord() (bindRecords []*BindRecord) {
return h.bindInfo.Load().(*bindCache).GetAllBindRecords()
Expand Down
14 changes: 14 additions & 0 deletions bindinfo/session_handle.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,25 @@ func (h *SessionHandle) DropBindRecord(originalSQL, db string, binding *Binding)
return nil
}

// DropBindRecordByDigest drop BindRecord in the cache.
func (h *SessionHandle) DropBindRecordByDigest(sqlDigest string) error {
oldRecord, err := h.GetBindRecordBySQLDigest(sqlDigest)
if err != nil {
return err
}
return h.DropBindRecord(oldRecord.OriginalSQL, strings.ToLower(oldRecord.Db), nil)
}

// GetBindRecord return the BindMeta of the (normdOrigSQL,db) if BindMeta exist.
func (h *SessionHandle) GetBindRecord(hash, normdOrigSQL, db string) *BindRecord {
return h.ch.GetBindRecord(hash, normdOrigSQL, db)
}

// GetBindRecordBySQLDigest return all BindMeta corresponding to hash.
func (h *SessionHandle) GetBindRecordBySQLDigest(sqlDigest string) (*BindRecord, error) {
return h.ch.GetBindRecordBySQLDigest(sqlDigest)
}

// GetAllBindRecord return all session bind info.
func (h *SessionHandle) GetAllBindRecord() (bindRecords []*BindRecord) {
return h.ch.GetAllBindRecords()
Expand Down
16 changes: 16 additions & 0 deletions executor/bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ func (e *SQLBindExec) Next(ctx context.Context, req *chunk.Chunk) error {
return e.createSQLBind()
case plannercore.OpSQLBindDrop:
return e.dropSQLBind()
case plannercore.OpSQLBindDropByDigest:
return e.dropSQLBindByDigest()
case plannercore.OpFlushBindings:
return e.flushBindings()
case plannercore.OpCaptureBindings:
Expand Down Expand Up @@ -85,6 +87,20 @@ func (e *SQLBindExec) dropSQLBind() error {
return err
}

func (e *SQLBindExec) dropSQLBindByDigest() error {
if e.sqlDigest == "" {
return errors.New("sql digest is empty")
}
if !e.isGlobal {
qw4990 marked this conversation as resolved.
Show resolved Hide resolved
handle := e.ctx.Value(bindinfo.SessionBindInfoKeyType).(*bindinfo.SessionHandle)
err := handle.DropBindRecordByDigest(e.sqlDigest)
return err
}
affectedRows, err := domain.GetDomain(e.ctx).BindHandle().DropBindRecordByDigest(e.sqlDigest)
e.ctx.GetSessionVars().StmtCtx.AddAffectedRows(affectedRows)
return err
}

func (e *SQLBindExec) setBindingStatus() error {
var bindInfo *bindinfo.Binding
if e.bindSQL != "" {
Expand Down
37 changes: 23 additions & 14 deletions parser/ast/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -1872,6 +1872,7 @@ type DropBindingStmt struct {
GlobalScope bool
OriginNode StmtNode
HintedNode StmtNode
SQLDigest string
}

func (n *DropBindingStmt) Restore(ctx *format.RestoreCtx) error {
Expand All @@ -1882,14 +1883,19 @@ func (n *DropBindingStmt) Restore(ctx *format.RestoreCtx) error {
ctx.WriteKeyWord("SESSION ")
}
ctx.WriteKeyWord("BINDING FOR ")
if err := n.OriginNode.Restore(ctx); err != nil {
return errors.Trace(err)
}
if n.HintedNode != nil {
ctx.WriteKeyWord(" USING ")
if err := n.HintedNode.Restore(ctx); err != nil {
if n.SQLDigest != "" {
ctx.WriteKeyWord("SQL DIGEST ")
ctx.WriteString(n.SQLDigest)
} else {
if err := n.OriginNode.Restore(ctx); err != nil {
return errors.Trace(err)
}
if n.HintedNode != nil {
ctx.WriteKeyWord(" USING ")
if err := n.HintedNode.Restore(ctx); err != nil {
return errors.Trace(err)
}
}
}
return nil
}
Expand All @@ -1900,17 +1906,20 @@ func (n *DropBindingStmt) Accept(v Visitor) (Node, bool) {
return v.Leave(newNode)
}
n = newNode.(*DropBindingStmt)
origNode, ok := n.OriginNode.Accept(v)
if !ok {
return n, false
}
n.OriginNode = origNode.(StmtNode)
if n.HintedNode != nil {
hintedNode, ok := n.HintedNode.Accept(v)
if n.SQLDigest == "" {
// if n.SQLDigest != "" means we build drop binding by sql digest
origNode, ok := n.OriginNode.Accept(v)
if !ok {
return n, false
}
n.HintedNode = hintedNode.(StmtNode)
n.OriginNode = origNode.(StmtNode)
if n.HintedNode != nil {
hintedNode, ok := n.HintedNode.Accept(v)
if !ok {
return n, false
}
n.HintedNode = hintedNode.(StmtNode)
}
}
return v.Leave(n)
}
Expand Down
1 change: 1 addition & 0 deletions parser/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ var tokenMap = map[string]int{
"DEPTH": depth,
"DESC": desc,
"DESCRIBE": describe,
"DIGEST": digest,
"DIRECTORY": directory,
"DISABLE": disable,
"DISABLED": disabled,
Expand Down
Loading