planner: clean BindRecord in the binding package (#50460)
ref pingcap/tidb#48875
This commit is contained in:
@ -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 {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user