Repository: pingcap/tidb
File: pkg/bindinfo/binding.go (Go)

Code before:
	}(time.Now())
	useBinding := sessionVars.UsePlanBaselines
	if !useBinding || stmtNode == nil {
		return
	}
	// When the domain is initializing, the bind will be nil.
	if sctx.Value(SessionBindInfoKeyType) == nil {
		return
	}
	cache := getMatchSQLBindingCache(sessionVars, stmtNode)
	if intest.InTest {
		binding, matched, scope = matchSQLBindingInternal(sctx, sessionVars, stmtNode, nil)
		assertMatchSQLBinding(cache, matched, binding, scope)
		return
	}
	if cache != nil {
		return cache.binding, cache.matched, cache.scope
	}
	return matchSQLBindingInternal(sctx, sessionVars, stmtNode, nil)
}

func matchSQLBindingForPlanCache(sctx sessionctx.Context, stmtNode ast.StmtNode, info *BindingMatchInfo) (binding *Binding, matched bool, scope string) {
	sessionVars := sctx.GetSessionVars()
	defer func(begin time.Time) {
		sessionVars.DurationOptimizer.BindingMatch = time.Since(begin)
	}(time.Now())
	useBinding := sessionVars.UsePlanBaselines
	if !useBinding || stmtNode == nil {
		return
	}
	// When the domain is initializing, the bind will be nil.
	if sctx.Value(SessionBindInfoKeyType) == nil {
		return
	}
	cache := getMatchSQLBindingCache(sessionVars, stmtNode)
	if intest.InTest {
		binding, matched, scope = matchSQLBindingInternal(sctx, sessionVars, stmtNode, info)
		assertMatchSQLBinding(cache, matched, binding, scope)
		return
	}
	if cache != nil {
		return cache.binding, cache.matched, cache.scope
	}
	return matchSQLBindingInternal(sctx, sessionVars, stmtNode, info)
}

func matchSQLBindingInternal(sctx sessionctx.Context, sessionVars *variable.SessionVars, stmtNode ast.StmtNode, info *BindingMatchInfo) (binding *Binding, matched bool, scope string) {
	// record the normalization result into info to avoid repeat normalization next time.
	var noDBDigest string
	var tableNames []*ast.TableName
	if info == nil || info.TableNames == nil || info.NoDBDigest == "" {

Review comment (performance):
`matchSQLBindingForPlanCache` duplicates almost all logic of `matchSQLBinding` (duration tracking, UsePlanBaselines checks, StmtCtx cache handling). This increases maintenance risk because future fixes may be applied to one path but not the other. Consider refactoring into a single helper that takes an optional `*BindingMatchInfo` (nil for the non-plan-cache path), and have both exported entry points call it.

Code after:
	sessionVars.StmtCtx.MatchSQLBindingCache = &BindingCacheItem{
		binding: nil,
		matched: false}
}

func matchSQLBindingCore(sctx sessionctx.Context, sessionVars *variable.SessionVars, stmtNode ast.StmtNode, info *BindingMatchInfo) (binding *Binding, matched bool, scope string) {
	defer func(begin time.Time) {
		sessionVars.DurationOptimizer.BindingMatch = time.Since(begin)
	}(time.Now())
	// record the normalization result into info to avoid repeat normalization next time.
	var noDBDigest string
	var tableNames []*ast.TableName
	if info == nil || info.TableNames == nil || info.NoDBDigest == "" {
		_, noDBDigest = NormalizeStmtForBinding(stmtNode, "", true)
		tableNames = CollectTableNames(stmtNode)
		if info != nil {
			info.NoDBDigest = noDBDigest
			info.TableNames = tableNames
		}
	} else {
		noDBDigest = info.NoDBDigest
		tableNames = info.TableNames
	}

	sessionHandle := sctx.Value(SessionBindInfoKeyType).(SessionBindingHandle)
	if binding, matched := sessionHandle.MatchSessionBinding(sctx, noDBDigest, tableNames); matched {
		setMatchSQLBindingCache(sessionVars, stmtNode, matched, binding, metrics.ScopeSession)
		return binding, matched, metrics.ScopeSession
	}
	globalHandle := GetBindingHandle(sctx)
	if globalHandle == nil {
		return
	}
	binding, matched = globalHandle.MatchingBinding(sctx, noDBDigest, tableNames)
	if matched {
		if vardef.EnableBindingUsage.Load() {
			// After hitting the cache, update the usage time of the bind.
			binding.UpdateLastUsedAt()
		}
		setMatchSQLBindingCache(sessionVars, stmtNode, matched, binding, metrics.ScopeGlobal)
		return binding, matched, metrics.ScopeGlobal
	}
	setMatchSQLBindingCache(sessionVars, stmtNode, false, nil, "")
	return
}

func assertMatchSQLBinding(cache *BindingCacheItem, hit bool, binding *Binding, scope string) {
	intest.AssertFunc(func() bool {
		if cache == nil {
			return true
		}