planner: clean BindRecord in the binding package (#50460)

ref pingcap/tidb#48875
This commit is contained in:
Yuanjia Zhang
2024-01-16 15:57:16 +08:00
committed by GitHub
parent f4ba6d71ef
commit 8a529303ce
13 changed files with 192 additions and 270 deletions

View File

@ -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 {

View File

@ -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) {

View File

@ -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))
}

View File

@ -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) {

View File

@ -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
}

View File

@ -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)

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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) {

View File

@ -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,

View File

@ -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 {

View File

@ -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()

View File

@ -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) {