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

Code before:

type bindingInfoUsageInfo struct {
	// LastUsedAt records the last time when this binding is used.
	// It is nil if this binding has never been used.
	// It is updated when this binding is used.
	// It is used to update the `last_used_time` field in mysql.bind_info table.
	LastUsedAt atomic.Pointer[time.Time]
	// LastSavedAt records the last time when this binding is saved into storage.
	LastSavedAt atomic.Pointer[time.Time]
}

var (
	// GetBindingHandle is a function to get the global binding handle.
	// It is mainly used to resolve cycle import issue.
	GetBindingHandle func(sctx sessionctx.Context) BindingHandle
)

// BindingMatchInfo records necessary information for cross-db binding matching.
// This is mainly for plan cache to avoid normalizing the same statement repeatedly.
type BindingMatchInfo struct {
	NoDBDigest string
	TableNames []*ast.TableName
}

// MatchSQLBindingForPlanCache matches binding for plan cache.
func MatchSQLBindingForPlanCache(sctx sessionctx.Context, stmtNode ast.StmtNode, info *BindingMatchInfo) (bindingSQL string) {
	binding, matched, _ := matchSQLBindingForPlanCache(sctx, stmtNode, info)
	if matched {
		bindingSQL = binding.BindSQL
	}
	return
}

// MatchSQLBinding returns the matched binding for this statement.
func MatchSQLBinding(sctx sessionctx.Context, stmtNode ast.StmtNode) (binding *Binding, matched bool, scope string) {
	return matchSQLBinding(sctx, stmtNode)
}

func getMatchSQLBindingCache(sessionVars *variable.SessionVars, stmtNode ast.StmtNode) (cache *BindingCacheItem) {
	if item := sessionVars.StmtCtx.MatchSQLBindingCacheKey; item != nil && item == stmtNode {
		// We only use this temp bindinfo cache once to avoid retaining it after drop prepare.
		cache = sessionVars.StmtCtx.MatchSQLBindingCache.(*BindingCacheItem)
		intest.Assert(sessionVars.StmtCtx.MatchSQLBindingCache != nil)
		return cache
	}
	return
}

func setMatchSQLBindingCache(sessionVars *variable.SessionVars, stmtNode ast.StmtNode, matched bool, binding *Binding, scope string) {
	if matched {
		sessionVars.StmtCtx.MatchSQLBindingCacheKey = stmtNode

Review comment (performance):
Could we merge `matchSQLBindingForPlanCache` and `MatchSQLBindingForPlanCache` into a single function...

Code after:

type bindingInfoUsageInfo struct {
	// LastUsedAt records the last time when this binding is used.
	// It is nil if this binding has never been used.
	// It is updated when this binding is used.
	// It is used to update the `last_used_time` field in mysql.bind_info table.
	LastUsedAt atomic.Pointer[time.Time]
	// LastSavedAt records the last time when this binding is saved into storage.
	LastSavedAt atomic.Pointer[time.Time]
}

var (
	// GetBindingHandle is a function to get the global binding handle.
	// It is mainly used to resolve cycle import issue.
	GetBindingHandle func(sctx sessionctx.Context) BindingHandle
)

// BindingMatchInfo records necessary information for cross-db binding matching.
// This is mainly for plan cache to avoid normalizing the same statement repeatedly.
type BindingMatchInfo struct {
	NoDBDigest string
	TableNames []*ast.TableName
}

// MatchSQLBinding returns the matched binding for this statement.
func MatchSQLBinding(sctx sessionctx.Context, stmtNode ast.StmtNode) (binding *Binding, matched bool, scope string) {
	sessionVars := sctx.GetSessionVars()
	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 = matchSQLBindingCore(sctx, sessionVars, stmtNode, nil)
		assertMatchSQLBinding(cache, matched, binding, scope)
		return
	}
	if cache != nil {
		return cache.binding, cache.matched, cache.scope
	}
	return matchSQLBindingCore(sctx, sessionVars, stmtNode, nil)
}

func getMatchSQLBindingCache(sessionVars *variable.SessionVars, stmtNode ast.StmtNode) (cache *BindingCacheItem) {
	if item := sessionVars.StmtCtx.MatchSQLBindingCacheKey; item != nil && item == stmtNode {
		// We only use this temp bindinfo cache once to avoid retaining it after drop prepare.
		cache = sessionVars.StmtCtx.MatchSQLBindingCache.(*BindingCacheItem)