diff --git a/pkg/bindinfo/bind_record.go b/pkg/bindinfo/bind_record.go index 96f9502410..93a11577f7 100644 --- a/pkg/bindinfo/bind_record.go +++ b/pkg/bindinfo/bind_record.go @@ -118,20 +118,13 @@ func (br *BindRecord) Copy() *BindRecord { return nbr } -// HasEnabledBinding checks if there are any enabled bindings in bind record. -func (br *BindRecord) HasEnabledBinding() bool { - for _, binding := range br.Bindings { - if binding.IsBindingEnabled() { - return true - } - } - return false -} - // HasAvailableBinding checks if there are any available bindings in bind record. // The available means the binding can be used or can be converted into a usable status. // It includes the 'Enabled', 'Using' and 'Disabled' status. -func (br *BindRecord) HasAvailableBinding() bool { +func HasAvailableBinding(br *BindRecord) bool { + if br == nil { + return false + } for _, binding := range br.Bindings { if binding.IsBindingAvailable() { return true @@ -140,17 +133,6 @@ func (br *BindRecord) HasAvailableBinding() bool { return false } -// FindEnabledBinding gets the enabled binding. -// There is at most one binding that can be used now. -func (br *BindRecord) FindEnabledBinding() *Binding { - for _, binding := range br.Bindings { - if binding.IsBindingEnabled() { - return &binding - } - } - return nil -} - // prepareHints builds ID and Hint for BindRecord. If sctx is not nil, we check if // the BindSQL is still valid. func prepareHints(sctx sessionctx.Context, binding *Binding) error { @@ -205,7 +187,7 @@ func merge(lBindRecord, rBindRecord *BindRecord) *BindRecord { if rBindRecord == nil { return lBindRecord } - result := lBindRecord.shallowCopy() + result := lBindRecord.Copy() for i := range rBindRecord.Bindings { rbind := rBindRecord.Bindings[i] found := false @@ -225,7 +207,7 @@ func merge(lBindRecord, rBindRecord *BindRecord) *BindRecord { return result } -func (br *BindRecord) removeDeletedBindings() *BindRecord { +func removeDeletedBindings(br *BindRecord) *BindRecord { result := BindRecord{Bindings: make([]Binding, 0, len(br.Bindings))} for _, binding := range br.Bindings { if binding.Status != deleted { @@ -235,15 +217,6 @@ func (br *BindRecord) removeDeletedBindings() *BindRecord { return &result } -// shallowCopy shallow copies the BindRecord. -func (br *BindRecord) shallowCopy() *BindRecord { - result := BindRecord{ - Bindings: make([]Binding, len(br.Bindings)), - } - copy(result.Bindings, br.Bindings) - return &result -} - // size calculates the memory size of a BindRecord. func (br *BindRecord) size() float64 { mem := float64(0) @@ -259,7 +232,7 @@ var statusIndex = map[string]int{ Invalid: 2, } -func (br *BindRecord) metrics() ([]float64, []int) { +func bindingMetrics(br *BindRecord) ([]float64, []int) { sizes := make([]float64, len(statusIndex)) count := make([]int, len(statusIndex)) if br == nil { @@ -288,8 +261,8 @@ func (b *Binding) size() float64 { } func updateMetrics(scope string, before *BindRecord, after *BindRecord, sizeOnly bool) { - beforeSize, beforeCount := before.metrics() - afterSize, afterCount := after.metrics() + beforeSize, beforeCount := bindingMetrics(before) + afterSize, afterCount := bindingMetrics(after) for status, index := range statusIndex { metrics.BindMemoryUsage.WithLabelValues(scope, status).Add(afterSize[index] - beforeSize[index]) if !sizeOnly { diff --git a/pkg/bindinfo/binding_match.go b/pkg/bindinfo/binding_match.go index 209ade24c3..bb18ad47bf 100644 --- a/pkg/bindinfo/binding_match.go +++ b/pkg/bindinfo/binding_match.go @@ -39,33 +39,27 @@ type BindingMatchInfo struct { // MatchSQLBindingForPlanCache matches binding for plan cache. func MatchSQLBindingForPlanCache(sctx sessionctx.Context, stmtNode ast.StmtNode, info *BindingMatchInfo) (bindingSQL string, ignoreBinding bool) { - bindRecord, _ := getBindRecord(sctx, stmtNode, info) - if bindRecord != nil { - if enabledBinding := bindRecord.FindEnabledBinding(); enabledBinding != nil { - bindingSQL = enabledBinding.BindSQL - ignoreBinding = enabledBinding.Hint.ContainTableHint(hint.HintIgnorePlanCache) - } + binding, matched, _ := matchSQLBinding(sctx, stmtNode, info) + if matched { + bindingSQL = binding.BindSQL + ignoreBinding = binding.Hint.ContainTableHint(hint.HintIgnorePlanCache) } return } // MatchSQLBinding returns the matched binding for this statement. -func MatchSQLBinding(sctx sessionctx.Context, stmtNode ast.StmtNode) (bindRecord *BindRecord, scope string, matched bool) { - bindRecord, scope = getBindRecord(sctx, stmtNode, nil) - if bindRecord == nil || len(bindRecord.Bindings) == 0 { - return nil, "", false - } - return bindRecord, scope, true +func MatchSQLBinding(sctx sessionctx.Context, stmtNode ast.StmtNode) (binding Binding, matched bool, scope string) { + return matchSQLBinding(sctx, stmtNode, nil) } -func getBindRecord(sctx sessionctx.Context, stmtNode ast.StmtNode, info *BindingMatchInfo) (*BindRecord, string) { +func matchSQLBinding(sctx sessionctx.Context, stmtNode ast.StmtNode, info *BindingMatchInfo) (binding Binding, matched bool, scope string) { useBinding := sctx.GetSessionVars().UsePlanBaselines if !useBinding || stmtNode == nil { - return nil, "" + return } // When the domain is initializing, the bind will be nil. if sctx.Value(SessionBindInfoKeyType) == nil { - return nil, "" + return } // record the normalization result into info to avoid repeat normalization next time. @@ -84,17 +78,17 @@ func getBindRecord(sctx sessionctx.Context, stmtNode ast.StmtNode, info *Binding } sessionHandle := sctx.Value(SessionBindInfoKeyType).(SessionBindingHandle) - if bindRecord, err := sessionHandle.MatchSessionBinding(sctx, fuzzyDigest, tableNames); err == nil && bindRecord != nil && bindRecord.HasEnabledBinding() { - return bindRecord, metrics.ScopeSession + if binding, matched := sessionHandle.MatchSessionBinding(sctx, fuzzyDigest, tableNames); matched { + return binding, matched, metrics.ScopeSession } globalHandle := GetGlobalBindingHandle(sctx) if globalHandle == nil { - return nil, "" + return } - if bindRecord, err := globalHandle.MatchGlobalBinding(sctx, fuzzyDigest, tableNames); err == nil && bindRecord != nil && bindRecord.HasEnabledBinding() { - return bindRecord, metrics.ScopeGlobal + if binding, matched := globalHandle.MatchGlobalBinding(sctx, fuzzyDigest, tableNames); matched { + return binding, matched, metrics.ScopeGlobal } - return nil, "" + return } func fuzzyMatchBindingTableName(currentDB string, stmtTableNames, bindingTableNames []*ast.TableName) (numWildcards int, matched bool) { diff --git a/pkg/bindinfo/capture.go b/pkg/bindinfo/capture.go index 0d386e1cc4..f65db8b73d 100644 --- a/pkg/bindinfo/capture.go +++ b/pkg/bindinfo/capture.go @@ -171,7 +171,7 @@ func (h *globalBindingHandle) CaptureBaselines() { } dbName := utilparser.GetDefaultDB(stmt, bindableStmt.Schema) normalizedSQL, digest := parser.NormalizeDigest(utilparser.RestoreWithDefaultDB(stmt, dbName, bindableStmt.Query)) - if r := h.getCache().GetBinding(digest.String()); r != nil && r.HasAvailableBinding() { + if r := h.getCache().GetBinding(digest.String()); HasAvailableBinding(r) { continue } bindSQL := GenerateBindSQL(context.TODO(), stmt, bindableStmt.PlanHint, true, dbName) @@ -195,7 +195,7 @@ func (h *globalBindingHandle) CaptureBaselines() { SQLDigest: digest.String(), } // We don't need to pass the `sctx` because the BindSQL has been validated already. - err = h.CreateGlobalBinding(nil, &binding) + err = h.CreateGlobalBinding(nil, binding) if err != nil { logutil.BgLogger().Debug("create bind record failed in baseline capture", zap.String("category", "sql-bind"), zap.String("SQL", bindableStmt.Query), zap.Error(err)) } diff --git a/pkg/bindinfo/capture_test.go b/pkg/bindinfo/capture_test.go index 3a75ab65df..c8e94ff3ef 100644 --- a/pkg/bindinfo/capture_test.go +++ b/pkg/bindinfo/capture_test.go @@ -331,13 +331,10 @@ func TestBindingSource(t *testing.T) { bindHandle := dom.BindHandle() stmt, _, _ := internal.UtilNormalizeWithDefaultDB(t, "select * from t where a > ?") _, 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) - require.Equal(t, "select * from `test` . `t` where `a` > ?", bindData.Bindings[0].OriginalSQL) - require.Len(t, bindData.Bindings, 1) - bind := bindData.Bindings[0] - require.Equal(t, bindinfo.Manual, bind.Source) + 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() @@ -353,13 +350,10 @@ func TestBindingSource(t *testing.T) { bindHandle.CaptureBaselines() stmt, _, _ = internal.UtilNormalizeWithDefaultDB(t, "select * from t where a < ?") _, 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) - require.Equal(t, "select * from `test` . `t` where `a` < ?", bindData.Bindings[0].OriginalSQL) - require.Len(t, bindData.Bindings, 1) - bind = bindData.Bindings[0] - require.Equal(t, bindinfo.Capture, bind.Source) + 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) { diff --git a/pkg/bindinfo/global_handle.go b/pkg/bindinfo/global_handle.go index 78f6605c4e..8d6da6cf2b 100644 --- a/pkg/bindinfo/global_handle.go +++ b/pkg/bindinfo/global_handle.go @@ -47,14 +47,14 @@ type GlobalBindingHandle interface { // Methods for create, get, drop global sql bindings. // MatchGlobalBinding returns the matched binding for this statement. - MatchGlobalBinding(sctx sessionctx.Context, fuzzyDigest string, tableNames []*ast.TableName) (*BindRecord, error) + MatchGlobalBinding(sctx sessionctx.Context, fuzzyDigest string, tableNames []*ast.TableName) (matchedBinding Binding, isMatched bool) // GetAllGlobalBindings returns all bind records in cache. - GetAllGlobalBindings() (bindings []*Binding) + GetAllGlobalBindings() (bindings []Binding) // CreateGlobalBinding creates a BindRecord to the storage and the cache. // It replaces all the exists bindings for the same normalized SQL. - CreateGlobalBinding(sctx sessionctx.Context, binding *Binding) (err error) + CreateGlobalBinding(sctx sessionctx.Context, binding Binding) (err error) // DropGlobalBinding drop BindRecord to the storage and BindRecord int the cache. DropGlobalBinding(sqlDigest string) (deletedRows uint64, err error) @@ -63,7 +63,7 @@ type GlobalBindingHandle interface { SetGlobalBindingStatus(newStatus, sqlDigest string) (ok bool, err error) // AddInvalidGlobalBinding adds BindRecord which needs to be deleted into invalidBindRecordMap. - AddInvalidGlobalBinding(invalidBinding *Binding) + AddInvalidGlobalBinding(invalidBinding Binding) // DropInvalidGlobalBinding executes the drop BindRecord tasks. DropInvalidGlobalBinding() @@ -151,7 +151,7 @@ const ( ) type bindRecordUpdate struct { - binding *Binding + binding Binding updateTime time.Time } @@ -201,7 +201,7 @@ func buildFuzzyDigestMap(bindRecords []*BindRecord) map[string][]string { func (h *globalBindingHandle) Reset() { h.lastUpdateTime.Store(types.ZeroTimestamp) h.invalidBindRecordMap.Value.Store(make(map[string]*bindRecordUpdate)) - h.invalidBindRecordMap.flushFunc = func(binding *Binding) error { + h.invalidBindRecordMap.flushFunc = func(binding Binding) error { if _, err := h.dropGlobalBinding(binding.SQLDigest); err != nil { return err } @@ -272,7 +272,7 @@ func (h *globalBindingHandle) LoadFromStorageToCache(fullLoad bool) (err error) } oldRecord := newCache.GetBinding(sqlDigest) - newRecord := merge(oldRecord, &BindRecord{Bindings: []Binding{*binding}}).removeDeletedBindings() + newRecord := removeDeletedBindings(merge(oldRecord, &BindRecord{Bindings: []Binding{*binding}})) if len(newRecord.Bindings) > 0 { err = newCache.SetBinding(sqlDigest, newRecord) if err != nil { @@ -292,8 +292,8 @@ func (h *globalBindingHandle) LoadFromStorageToCache(fullLoad bool) (err error) // CreateGlobalBinding creates a BindRecord to the storage and the cache. // It replaces all the exists bindings for the same normalized SQL. -func (h *globalBindingHandle) CreateGlobalBinding(sctx sessionctx.Context, binding *Binding) (err error) { - if err := prepareHints(sctx, binding); err != nil { +func (h *globalBindingHandle) CreateGlobalBinding(sctx sessionctx.Context, binding Binding) (err error) { + if err := prepareHints(sctx, &binding); err != nil { return err } defer func() { @@ -448,7 +448,7 @@ func lockBindInfoTable(sctx sessionctx.Context) error { type tmpBindRecordMap struct { sync.Mutex atomic.Value - flushFunc func(binding *Binding) error + flushFunc func(binding Binding) error } // flushToStore calls flushFunc for items in tmpBindRecordMap and removes them with a delay. @@ -468,14 +468,14 @@ func (tmpMap *tmpBindRecordMap) flushToStore() { if time.Since(bindRecord.updateTime) > 6*time.Second { delete(newMap, key) - updateMetrics(metrics.ScopeGlobal, &BindRecord{Bindings: []Binding{*bindRecord.binding}}, nil, false) + updateMetrics(metrics.ScopeGlobal, &BindRecord{Bindings: []Binding{bindRecord.binding}}, nil, false) } } tmpMap.Store(newMap) } // Add puts a BindRecord into tmpBindRecordMap. -func (tmpMap *tmpBindRecordMap) Add(binding *Binding) { +func (tmpMap *tmpBindRecordMap) Add(binding Binding) { key := binding.OriginalSQL + ":" + binding.Db + ":" + binding.ID if _, ok := tmpMap.Load().(map[string]*bindRecordUpdate)[key]; ok { return @@ -490,7 +490,7 @@ func (tmpMap *tmpBindRecordMap) Add(binding *Binding) { binding: binding, } tmpMap.Store(newMap) - updateMetrics(metrics.ScopeGlobal, nil, &BindRecord{Bindings: []Binding{*binding}}, false) + updateMetrics(metrics.ScopeGlobal, nil, &BindRecord{Bindings: []Binding{binding}}, false) } // DropInvalidGlobalBinding executes the drop BindRecord tasks. @@ -504,7 +504,7 @@ func (h *globalBindingHandle) DropInvalidGlobalBinding() { } // AddInvalidGlobalBinding adds BindRecord which needs to be deleted into invalidBindRecordMap. -func (h *globalBindingHandle) AddInvalidGlobalBinding(invalidBinding *Binding) { +func (h *globalBindingHandle) AddInvalidGlobalBinding(invalidBinding Binding) { h.invalidBindRecordMap.Add(invalidBinding) } @@ -515,17 +515,16 @@ func (h *globalBindingHandle) Size() int { } // MatchGlobalBinding returns the matched binding for this statement. -func (h *globalBindingHandle) MatchGlobalBinding(sctx sessionctx.Context, fuzzyDigest string, tableNames []*ast.TableName) (*BindRecord, error) { +func (h *globalBindingHandle) MatchGlobalBinding(sctx sessionctx.Context, fuzzyDigest string, tableNames []*ast.TableName) (matchedBinding Binding, isMatched bool) { bindingCache := h.getCache() if bindingCache.Size() == 0 { - return nil, nil + return } fuzzyDigestMap := h.getFuzzyDigestMap() if len(fuzzyDigestMap) == 0 { - return nil, nil + return } - var bestBinding *BindRecord leastWildcards := len(tableNames) + 1 for _, exactDigest := range fuzzyDigestMap[fuzzyDigest] { sqlDigest := exactDigest @@ -536,23 +535,21 @@ func (h *globalBindingHandle) MatchGlobalBinding(sctx sessionctx.Context, fuzzyD continue // fuzzy binding is disabled, skip this binding } if matched && numWildcards < leastWildcards { - bestBinding = bindRecord + matchedBinding = binding + isMatched = true leastWildcards = numWildcards break } } } } - - return bestBinding, nil + return } // GetAllGlobalBindings returns all bind records in cache. -func (h *globalBindingHandle) GetAllGlobalBindings() (bindings []*Binding) { +func (h *globalBindingHandle) GetAllGlobalBindings() (bindings []Binding) { for _, record := range h.getCache().GetAllBindings() { - for i := range record.Bindings { - bindings = append(bindings, &record.Bindings[i]) - } + bindings = append(bindings, record.Bindings...) } return } diff --git a/pkg/bindinfo/global_handle_test.go b/pkg/bindinfo/global_handle_test.go index 2d66f79f2c..1000ab0d62 100644 --- a/pkg/bindinfo/global_handle_test.go +++ b/pkg/bindinfo/global_handle_test.go @@ -72,11 +72,9 @@ func TestBindingLastUpdateTime(t *testing.T) { require.NoError(t, err) _, 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)) - bind := bindData.Bindings[0] - updateTime := bind.UpdateTime.String() + binding, matched := bindHandle.MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) + require.True(t, matched) + updateTime := binding.UpdateTime.String() rows1 := tk.MustQuery("show status like 'last_plan_binding_update_time';").Rows() updateTime1 := rows1[0][1] @@ -141,19 +139,17 @@ func TestBindParse(t *testing.T) { stmt, err := parser.New().ParseOneStmt("select * from test . t", "", "") require.NoError(t, err) _, 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) - bind := bindData.Bindings[0] - require.Equal(t, "select * from `test` . `t`", bind.OriginalSQL) - require.Equal(t, "select * from `test` . `t` use index(index_t)", bind.BindSQL) - require.Equal(t, "test", bind.Db) - require.Equal(t, bindinfo.Enabled, bind.Status) - require.Equal(t, "utf8mb4", bind.Charset) - require.Equal(t, "utf8mb4_bin", bind.Collation) - require.NotNil(t, bind.CreateTime) - require.NotNil(t, bind.UpdateTime) - dur, err := bind.SinceUpdateTime() + binding, matched := bindHandle.MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) + require.True(t, matched) + require.Equal(t, "select * from `test` . `t`", binding.OriginalSQL) + require.Equal(t, "select * from `test` . `t` use index(index_t)", binding.BindSQL) + require.Equal(t, "test", binding.Db) + require.Equal(t, bindinfo.Enabled, binding.Status) + require.Equal(t, "utf8mb4", binding.Charset) + require.Equal(t, "utf8mb4_bin", binding.Collation) + require.NotNil(t, binding.CreateTime) + require.NotNil(t, binding.UpdateTime) + dur, err := binding.SinceUpdateTime() require.NoError(t, err) require.GreaterOrEqual(t, int64(dur), int64(0)) @@ -443,18 +439,16 @@ func TestGlobalBinding(t *testing.T) { stmt, _, _ := internal.UtilNormalizeWithDefaultDB(t, testSQL.querySQL) _, 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) - bind := bindData.Bindings[0] - require.Equal(t, testSQL.originSQL, bind.OriginalSQL) - require.Equal(t, testSQL.bindSQL, bind.BindSQL) - require.Equal(t, "test", bind.Db) - require.Equal(t, bindinfo.Enabled, bind.Status) - require.NotNil(t, bind.Charset) - require.NotNil(t, bind.Collation) - require.NotNil(t, bind.CreateTime) - require.NotNil(t, bind.UpdateTime) + binding, matched := dom.BindHandle().MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) + require.True(t, matched) + require.Equal(t, testSQL.originSQL, binding.OriginalSQL) + require.Equal(t, testSQL.bindSQL, binding.BindSQL) + require.Equal(t, "test", binding.Db) + require.Equal(t, bindinfo.Enabled, binding.Status) + require.NotNil(t, binding.Charset) + require.NotNil(t, binding.Collation) + require.NotNil(t, binding.CreateTime) + require.NotNil(t, binding.UpdateTime) rs, err := tk.Exec("show global bindings") require.NoError(t, err) @@ -478,26 +472,23 @@ func TestGlobalBinding(t *testing.T) { require.Equal(t, 1, bindHandle.Size()) _, 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) - bind = bindData.Bindings[0] - require.Equal(t, testSQL.originSQL, bind.OriginalSQL) - require.Equal(t, testSQL.bindSQL, bind.BindSQL) - require.Equal(t, "test", bind.Db) - require.Equal(t, bindinfo.Enabled, bind.Status) - require.NotNil(t, bind.Charset) - require.NotNil(t, bind.Collation) - require.NotNil(t, bind.CreateTime) - require.NotNil(t, bind.UpdateTime) + binding, matched = dom.BindHandle().MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) + require.True(t, matched) + require.Equal(t, testSQL.originSQL, binding.OriginalSQL) + require.Equal(t, testSQL.bindSQL, binding.BindSQL) + require.Equal(t, "test", binding.Db) + require.Equal(t, bindinfo.Enabled, binding.Status) + require.NotNil(t, binding.Charset) + require.NotNil(t, binding.Collation) + require.NotNil(t, binding.CreateTime) + require.NotNil(t, binding.UpdateTime) _, err = tk.Exec("drop global " + testSQL.dropSQL) require.Equal(t, uint64(1), tk.Session().AffectedRows()) require.NoError(t, err) _, 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) + _, matched = dom.BindHandle().MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) + require.False(t, matched) // dropped bindHandle = bindinfo.NewGlobalBindingHandle(&mockSessionPool{tk.Session()}) err = bindHandle.LoadFromStorageToCache(true) @@ -505,9 +496,8 @@ func TestGlobalBinding(t *testing.T) { require.Equal(t, 0, bindHandle.Size()) _, 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) + _, matched = dom.BindHandle().MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) + require.False(t, matched) // dropped rs, err = tk.Exec("show global bindings") require.NoError(t, err) diff --git a/pkg/bindinfo/session_handle.go b/pkg/bindinfo/session_handle.go index a4b154d5db..68c06a6a6c 100644 --- a/pkg/bindinfo/session_handle.go +++ b/pkg/bindinfo/session_handle.go @@ -37,16 +37,16 @@ import ( // SessionBindingHandle is used to handle all session sql bind operations. type SessionBindingHandle interface { // CreateSessionBinding creates a binding to the cache. - CreateSessionBinding(sctx sessionctx.Context, binding *Binding) (err error) + CreateSessionBinding(sctx sessionctx.Context, binding Binding) (err error) // DropSessionBinding drops a binding by the sql digest. DropSessionBinding(sqlDigest string) error // MatchSessionBinding returns the matched binding for this statement. - MatchSessionBinding(sctx sessionctx.Context, fuzzyDigest string, tableNames []*ast.TableName) (*BindRecord, error) + MatchSessionBinding(sctx sessionctx.Context, fuzzyDigest string, tableNames []*ast.TableName) (matchedBinding Binding, isMatched bool) // GetAllSessionBindings return all bindings. - GetAllSessionBindings() (bindings []*Binding) + GetAllSessionBindings() (bindings []Binding) // Close closes the SessionBindingHandle. Close() @@ -79,8 +79,8 @@ func (h *sessionBindingHandle) appendSessionBinding(sqlDigest string, meta *Bind // CreateSessionBinding creates a BindRecord to the cache. // It replaces all the exists bindings for the same normalized SQL. -func (h *sessionBindingHandle) CreateSessionBinding(sctx sessionctx.Context, binding *Binding) (err error) { - if err := prepareHints(sctx, binding); err != nil { +func (h *sessionBindingHandle) CreateSessionBinding(sctx sessionctx.Context, binding Binding) (err error) { + if err := prepareHints(sctx, &binding); err != nil { return err } binding.Db = strings.ToLower(binding.Db) @@ -89,7 +89,7 @@ func (h *sessionBindingHandle) CreateSessionBinding(sctx sessionctx.Context, bin binding.UpdateTime = now // update the BindMeta to the cache. - h.appendSessionBinding(parser.DigestNormalized(binding.OriginalSQL).String(), &BindRecord{Bindings: []Binding{*binding}}) + h.appendSessionBinding(parser.DigestNormalized(binding.OriginalSQL).String(), &BindRecord{Bindings: []Binding{binding}}) return nil } @@ -103,17 +103,16 @@ func (h *sessionBindingHandle) DropSessionBinding(sqlDigest string) error { } // MatchSessionBinding returns the matched binding for this statement. -func (h *sessionBindingHandle) MatchSessionBinding(sctx sessionctx.Context, fuzzyDigest string, tableNames []*ast.TableName) (*BindRecord, error) { +func (h *sessionBindingHandle) MatchSessionBinding(sctx sessionctx.Context, fuzzyDigest string, tableNames []*ast.TableName) (matchedBinding Binding, isMatched bool) { // The current implementation is simplistic, but session binding is only for test purpose, so // there shouldn't be many session bindings, and to keep it simple, this implementation is acceptable. - var bestBinding *BindRecord leastWildcards := len(tableNames) + 1 bindRecords := h.ch.GetAllBindings() for _, bindRecord := range bindRecords { for _, binding := range bindRecord.Bindings { bindingStmt, err := parser.New().ParseOneStmt(binding.BindSQL, binding.Charset, binding.Collation) if err != nil { - return nil, err + return } _, bindingFuzzyDigest := norm.NormalizeStmtForBinding(bindingStmt, norm.WithFuzz(true)) if bindingFuzzyDigest != fuzzyDigest { @@ -126,21 +125,20 @@ func (h *sessionBindingHandle) MatchSessionBinding(sctx sessionctx.Context, fuzz continue // fuzzy binding is disabled, skip this binding } if matched && numWildcards < leastWildcards { - bestBinding = bindRecord + matchedBinding = binding + isMatched = true leastWildcards = numWildcards break } } } - return bestBinding, nil + return } // GetAllSessionBindings return all session bind info. -func (h *sessionBindingHandle) GetAllSessionBindings() (bindings []*Binding) { +func (h *sessionBindingHandle) GetAllSessionBindings() (bindings []Binding) { for _, record := range h.ch.GetAllBindings() { - for i := range record.Bindings { - bindings = append(bindings, &record.Bindings[i]) - } + bindings = append(bindings, record.Bindings...) } return } diff --git a/pkg/bindinfo/session_handle_test.go b/pkg/bindinfo/session_handle_test.go index 660db8da84..1769164bf9 100644 --- a/pkg/bindinfo/session_handle_test.go +++ b/pkg/bindinfo/session_handle_test.go @@ -115,18 +115,16 @@ func TestSessionBinding(t *testing.T) { require.NoError(t, err) _, 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) - bind := bindData.Bindings[0] - require.Equal(t, testSQL.originSQL, bind.OriginalSQL) - require.Equal(t, testSQL.bindSQL, bind.BindSQL) - require.Equal(t, "test", bind.Db) - require.Equal(t, bindinfo.Enabled, bind.Status) - require.NotNil(t, bind.Charset) - require.NotNil(t, bind.Collation) - require.NotNil(t, bind.CreateTime) - require.NotNil(t, bind.UpdateTime) + binding, matched := handle.MatchSessionBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) + require.True(t, matched) + require.Equal(t, testSQL.originSQL, binding.OriginalSQL) + require.Equal(t, testSQL.bindSQL, binding.BindSQL) + require.Equal(t, "test", binding.Db) + require.Equal(t, bindinfo.Enabled, binding.Status) + require.NotNil(t, binding.Charset) + require.NotNil(t, binding.Collation) + require.NotNil(t, binding.CreateTime) + require.NotNil(t, binding.UpdateTime) rs, err := tk.Exec("show global bindings") require.NoError(t, err) @@ -154,9 +152,8 @@ func TestSessionBinding(t *testing.T) { _, err = tk.Exec("drop session " + testSQL.dropSQL) require.NoError(t, err) _, 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 + _, matched = handle.MatchSessionBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) + require.False(t, matched) // dropped } } diff --git a/pkg/bindinfo/tests/bind_test.go b/pkg/bindinfo/tests/bind_test.go index eda33ae614..48f76227de 100644 --- a/pkg/bindinfo/tests/bind_test.go +++ b/pkg/bindinfo/tests/bind_test.go @@ -322,18 +322,16 @@ func TestBindingSymbolList(t *testing.T) { require.NoError(t, err) _, 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) - require.Equal(t, "select `a` , `b` from `test` . `t` where `a` = ? limit ...", bindData.Bindings[0].OriginalSQL) - bind := bindData.Bindings[0] - require.Equal(t, "SELECT `a`,`b` FROM `test`.`t` USE INDEX (`ib`) WHERE `a` = 1 LIMIT 0,1", bind.BindSQL) - require.Equal(t, "test", bind.Db) - require.Equal(t, bindinfo.Enabled, bind.Status) - require.NotNil(t, bind.Charset) - require.NotNil(t, bind.Collation) - require.NotNil(t, bind.CreateTime) - require.NotNil(t, bind.UpdateTime) + binding, matched := dom.BindHandle().MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) + require.True(t, matched) + require.Equal(t, "select `a` , `b` from `test` . `t` where `a` = ? limit ...", binding.OriginalSQL) + require.Equal(t, "SELECT `a`,`b` FROM `test`.`t` USE INDEX (`ib`) WHERE `a` = 1 LIMIT 0,1", binding.BindSQL) + require.Equal(t, "test", binding.Db) + require.Equal(t, bindinfo.Enabled, binding.Status) + require.NotNil(t, binding.Charset) + require.NotNil(t, binding.Collation) + require.NotNil(t, binding.CreateTime) + require.NotNil(t, binding.UpdateTime) } // TestBindingInListWithSingleLiteral tests sql with "IN (Lit)", fixes #44298 @@ -348,14 +346,14 @@ func TestBindingInListWithSingleLiteral(t *testing.T) { // GIVEN sqlcmd := "select a, b from t where a in (1)" - binding := `create global binding for select a, b from t where a in (1, 2, 3) using select a, b from t use index (ib) where a in (1, 2, 3)` + bindingStmt := `create global binding for select a, b from t where a in (1, 2, 3) using select a, b from t use index (ib) where a in (1, 2, 3)` // before binding tk.MustQuery(sqlcmd) require.Equal(t, "t:ia", tk.Session().GetSessionVars().StmtCtx.IndexNames[0]) require.True(t, tk.MustUseIndex(sqlcmd, "ia(a)")) - tk.MustExec(binding) + tk.MustExec(bindingStmt) // after binding tk.MustQuery(sqlcmd) @@ -369,18 +367,16 @@ func TestBindingInListWithSingleLiteral(t *testing.T) { require.NoError(t, err) _, 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) - require.Equal(t, "select `a` , `b` from `test` . `t` where `a` in ( ... )", bindData.Bindings[0].OriginalSQL) - bind := bindData.Bindings[0] - require.Equal(t, "SELECT `a`,`b` FROM `test`.`t` USE INDEX (`ib`) WHERE `a` IN (1,2,3)", bind.BindSQL) - require.Equal(t, "test", bind.Db) - require.Equal(t, bindinfo.Enabled, bind.Status) - require.NotNil(t, bind.Charset) - require.NotNil(t, bind.Collation) - require.NotNil(t, bind.CreateTime) - require.NotNil(t, bind.UpdateTime) + binding, matched := dom.BindHandle().MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) + require.True(t, matched) + require.Equal(t, "select `a` , `b` from `test` . `t` where `a` in ( ... )", binding.OriginalSQL) + require.Equal(t, "SELECT `a`,`b` FROM `test`.`t` USE INDEX (`ib`) WHERE `a` IN (1,2,3)", binding.BindSQL) + require.Equal(t, "test", binding.Db) + require.Equal(t, bindinfo.Enabled, binding.Status) + require.NotNil(t, binding.Charset) + require.NotNil(t, binding.Collation) + require.NotNil(t, binding.CreateTime) + require.NotNil(t, binding.UpdateTime) } func TestBestPlanInBaselines(t *testing.T) { @@ -407,14 +403,12 @@ func TestBestPlanInBaselines(t *testing.T) { stmt, _, _ := internal.UtilNormalizeWithDefaultDB(t, "select a, b from t where a = 1 limit 0, 1") _, 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) - require.Equal(t, "select `a` , `b` from `test` . `t` where `a` = ? limit ...", bindData.Bindings[0].OriginalSQL) - bind := bindData.Bindings[0] - require.Equal(t, "SELECT /*+ use_index(@`sel_1` `test`.`t` `ia`)*/ `a`,`b` FROM `test`.`t` WHERE `a` = 1 LIMIT 0,1", bind.BindSQL) - require.Equal(t, "test", bind.Db) - require.Equal(t, bindinfo.Enabled, bind.Status) + binding, matched := dom.BindHandle().MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) + require.True(t, matched) + require.Equal(t, "select `a` , `b` from `test` . `t` where `a` = ? limit ...", binding.OriginalSQL) + require.Equal(t, "SELECT /*+ use_index(@`sel_1` `test`.`t` `ia`)*/ `a`,`b` FROM `test`.`t` WHERE `a` = 1 LIMIT 0,1", binding.BindSQL) + require.Equal(t, "test", binding.Db) + require.Equal(t, bindinfo.Enabled, binding.Status) tk.MustQuery("select a, b from t where a = 3 limit 1, 10") require.Equal(t, "t:ia", tk.Session().GetSessionVars().StmtCtx.IndexNames[0]) @@ -443,18 +437,16 @@ func TestErrorBind(t *testing.T) { stmt, err := parser.New().ParseOneStmt("select * from test . t where i > ?", "", "") require.NoError(t, err) _, 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) - require.Equal(t, "select * from `test` . `t` where `i` > ?", bindData.Bindings[0].OriginalSQL) - bind := bindData.Bindings[0] - require.Equal(t, "SELECT * FROM `test`.`t` USE INDEX (`index_t`) WHERE `i` > 100", bind.BindSQL) - require.Equal(t, "test", bind.Db) - require.Equal(t, bindinfo.Enabled, bind.Status) - require.NotNil(t, bind.Charset) - require.NotNil(t, bind.Collation) - require.NotNil(t, bind.CreateTime) - require.NotNil(t, bind.UpdateTime) + binding, matched := dom.BindHandle().MatchGlobalBinding(tk.Session(), fuzzyDigest, bindinfo.CollectTableNames(stmt)) + require.True(t, matched) + require.Equal(t, "select * from `test` . `t` where `i` > ?", binding.OriginalSQL) + require.Equal(t, "SELECT * FROM `test`.`t` USE INDEX (`index_t`) WHERE `i` > 100", binding.BindSQL) + require.Equal(t, "test", binding.Db) + require.Equal(t, bindinfo.Enabled, binding.Status) + require.NotNil(t, binding.Charset) + require.NotNil(t, binding.Collation) + require.NotNil(t, binding.CreateTime) + require.NotNil(t, binding.UpdateTime) tk.MustExec("drop index index_t on t") rs, err := tk.Exec("select * from t where i > 10") @@ -502,70 +494,52 @@ func TestHintsSetID(t *testing.T) { stmt, err := parser.New().ParseOneStmt("select * from t where a > ?", "", "") require.NoError(t, err) _, 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) - require.Equal(t, "select * from `test` . `t` where `a` > ?", bindData.Bindings[0].OriginalSQL) - require.Len(t, bindData.Bindings, 1) - bind := bindData.Bindings[0] - require.Equal(t, "use_index(@`sel_1` `test`.`t` `idx_a`)", bind.ID) + binding, matched := dom.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, "use_index(@`sel_1` `test`.`t` `idx_a`)", binding.ID) 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 = 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) - require.Equal(t, "select * from `test` . `t` where `a` > ?", bindData.Bindings[0].OriginalSQL) - require.Len(t, bindData.Bindings, 1) - bind = bindData.Bindings[0] - require.Equal(t, "use_index(@`sel_1` `test`.`t` `idx_a`)", bind.ID) + binding, matched = dom.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, "use_index(@`sel_1` `test`.`t` `idx_a`)", binding.ID) 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 = 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) - require.Equal(t, "select * from `test` . `t` where `a` > ?", bindData.Bindings[0].OriginalSQL) - require.Len(t, bindData.Bindings, 1) - bind = bindData.Bindings[0] - require.Equal(t, "use_index(@`sel_1` `test`.`t` `idx_a`)", bind.ID) + binding, matched = dom.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, "use_index(@`sel_1` `test`.`t` `idx_a`)", binding.ID) 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 = 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) - require.Equal(t, "select * from `test` . `t` where `a` > ?", bindData.Bindings[0].OriginalSQL) - require.Len(t, bindData.Bindings, 1) - bind = bindData.Bindings[0] - require.Equal(t, "use_index(@`sel_1` `test`.`t` `idx_a`)", bind.ID) + binding, matched = dom.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, "use_index(@`sel_1` `test`.`t` `idx_a`)", binding.ID) 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 = 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) - require.Equal(t, "select * from `test` . `t` where `a` > ?", bindData.Bindings[0].OriginalSQL) - require.Len(t, bindData.Bindings, 1) - bind = bindData.Bindings[0] - require.Equal(t, "use_index(@`sel_1` `test`.`t` `idx_a`)", bind.ID) + binding, matched = dom.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, "use_index(@`sel_1` `test`.`t` `idx_a`)", binding.ID) internal.UtilCleanBindingEnv(tk, dom) 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 = 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) - require.Equal(t, "select * from `test` . `t` where `a` > ?", bindData.Bindings[0].OriginalSQL) - require.Len(t, bindData.Bindings, 1) - bind = bindData.Bindings[0] - require.Equal(t, "", bind.ID) + binding, matched = dom.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, "", binding.ID) } func TestBindingWithIsolationRead(t *testing.T) { diff --git a/pkg/executor/adapter.go b/pkg/executor/adapter.go index fa460efb10..c695a0ff5a 100644 --- a/pkg/executor/adapter.go +++ b/pkg/executor/adapter.go @@ -2161,7 +2161,7 @@ func sendPlanReplayerDumpTask(key replayer.PlanReplayerTaskKey, sctx sessionctx. bindings := handle.GetAllSessionBindings() bindRecords := make([]*bindinfo.BindRecord, 0, len(bindings)) for _, binding := range bindings { - bindRecords = append(bindRecords, &bindinfo.BindRecord{Bindings: []bindinfo.Binding{*binding}}) + bindRecords = append(bindRecords, &bindinfo.BindRecord{Bindings: []bindinfo.Binding{binding}}) } dumpTask := &domain.PlanReplayerDumpTask{ PlanReplayerTaskKey: key, diff --git a/pkg/executor/bind.go b/pkg/executor/bind.go index 8cea12a675..fef5c9d7ad 100644 --- a/pkg/executor/bind.go +++ b/pkg/executor/bind.go @@ -142,9 +142,9 @@ func (e *SQLBindExec) createSQLBind() error { } if !e.isGlobal { handle := e.Ctx().Value(bindinfo.SessionBindInfoKeyType).(bindinfo.SessionBindingHandle) - return handle.CreateSessionBinding(e.Ctx(), &binding) + return handle.CreateSessionBinding(e.Ctx(), binding) } - return domain.GetDomain(e.Ctx()).BindHandle().CreateGlobalBinding(e.Ctx(), &binding) + return domain.GetDomain(e.Ctx()).BindHandle().CreateGlobalBinding(e.Ctx(), binding) } func (e *SQLBindExec) flushBindings() error { diff --git a/pkg/executor/show.go b/pkg/executor/show.go index 6de2bf5acc..b8c2453079 100644 --- a/pkg/executor/show.go +++ b/pkg/executor/show.go @@ -321,7 +321,7 @@ func (*visibleChecker) Leave(in ast.Node) (out ast.Node, ok bool) { } func (e *ShowExec) fetchShowBind() error { - var bindings []*bindinfo.Binding + var bindings []bindinfo.Binding if !e.GlobalScope { handle := e.Ctx().Value(bindinfo.SessionBindInfoKeyType).(bindinfo.SessionBindingHandle) bindings = handle.GetAllSessionBindings() diff --git a/pkg/planner/optimize.go b/pkg/planner/optimize.go index 4a471d5fdc..7b5d6a6a3f 100644 --- a/pkg/planner/optimize.go +++ b/pkg/planner/optimize.go @@ -219,7 +219,12 @@ func Optimize(ctx context.Context, sctx sessionctx.Context, node ast.Node, is in enableUseBinding := sessVars.UsePlanBaselines stmtNode, isStmtNode := node.(ast.StmtNode) - bindRecord, scope, match := bindinfo.MatchSQLBinding(sctx, stmtNode) + binding, match, scope := bindinfo.MatchSQLBinding(sctx, stmtNode) + var bindRecord *bindinfo.BindRecord + if match { + bindRecord = &bindinfo.BindRecord{Bindings: []bindinfo.Binding{binding}} + } + useBinding := enableUseBinding && isStmtNode && match if sessVars.StmtCtx.EnableOptimizerDebugTrace { failpoint.Inject("SetBindingTimeToZero", func(val failpoint.Value) { @@ -566,7 +571,7 @@ func handleInvalidBinding(ctx context.Context, sctx sessionctx.Context, level st } globalHandle := domain.GetDomain(sctx).BindHandle() - globalHandle.AddInvalidGlobalBinding(&binding) + globalHandle.AddInvalidGlobalBinding(binding) } func handleStmtHints(hints []*ast.TableOptimizerHint) (stmtHints stmtctx.StmtHints, offs []int, warns []error) {