From 62d6f4737bfb2dcf80c30fe4d412b4deebb6979b Mon Sep 17 00:00:00 2001 From: Yuanjia Zhang Date: Sun, 7 Apr 2024 16:30:20 +0800 Subject: [PATCH] planner: move fields from ast.Prepared to planner.PlanCacheStmt (#52373) ref pingcap/tidb#51407 --- pkg/executor/adapter.go | 2 +- pkg/executor/compiler.go | 2 +- pkg/executor/prepared.go | 3 +- pkg/parser/ast/misc.go | 8 ++--- pkg/planner/core/plan_cache.go | 45 +++++++++++++--------------- pkg/planner/core/plan_cache_utils.go | 18 +++++++---- pkg/server/driver_tidb.go | 2 +- pkg/session/session.go | 11 ++++--- 8 files changed, 44 insertions(+), 47 deletions(-) diff --git a/pkg/executor/adapter.go b/pkg/executor/adapter.go index cedda22373..110dd386f3 100644 --- a/pkg/executor/adapter.go +++ b/pkg/executor/adapter.go @@ -291,7 +291,7 @@ func (a *ExecStmt) PointGet(ctx context.Context) (*recordSet, error) { a.PsStmt.Executor = nil } else { // CachedPlan type is already checked in last step - pointGetPlan := a.PsStmt.PreparedAst.CachedPlan.(*plannercore.PointGetPlan) + pointGetPlan := a.PsStmt.CachedPlan.(*plannercore.PointGetPlan) exec.Init(pointGetPlan) a.PsStmt.Executor = exec executor = exec diff --git a/pkg/executor/compiler.go b/pkg/executor/compiler.go index 7b974cdd8c..381d3b6204 100644 --- a/pkg/executor/compiler.go +++ b/pkg/executor/compiler.go @@ -140,7 +140,7 @@ func (c *Compiler) Compile(ctx context.Context, stmtNode ast.StmtNode) (_ *ExecS stmt.PsStmt = preparedObj } else { // invalid the previous cached point plan - preparedObj.PreparedAst.CachedPlan = nil + preparedObj.CachedPlan = nil } } } diff --git a/pkg/executor/prepared.go b/pkg/executor/prepared.go index 9bc69803f5..c9ae6edceb 100644 --- a/pkg/executor/prepared.go +++ b/pkg/executor/prepared.go @@ -204,11 +204,10 @@ func (e *DeallocateExec) Next(context.Context, *chunk.Chunk) error { if !ok { return errors.Errorf("invalid PlanCacheStmt type") } - prepared := preparedObj.PreparedAst delete(vars.PreparedStmtNameToID, e.Name) if e.Ctx().GetSessionVars().EnablePreparedPlanCache { bindSQL, _ := bindinfo.MatchSQLBindingForPlanCache(e.Ctx(), preparedObj.PreparedAst.Stmt, &preparedObj.BindingInfo) - cacheKey, err := plannercore.NewPlanCacheKey(vars, preparedObj.StmtText, preparedObj.StmtDB, prepared.SchemaVersion, + cacheKey, err := plannercore.NewPlanCacheKey(vars, preparedObj.StmtText, preparedObj.StmtDB, preparedObj.SchemaVersion, 0, bindSQL, expression.ExprPushDownBlackListReloadTimeStamp.Load()) if err != nil { return err diff --git a/pkg/parser/ast/misc.go b/pkg/parser/ast/misc.go index 3cc570e857..64185cf855 100644 --- a/pkg/parser/ast/misc.go +++ b/pkg/parser/ast/misc.go @@ -552,12 +552,8 @@ func (n *DeallocateStmt) Accept(v Visitor) (Node, bool) { // Prepared represents a prepared statement. type Prepared struct { - Stmt StmtNode - StmtType string - Params []ParamMarkerExpr - SchemaVersion int64 - CachedPlan interface{} - CachedNames interface{} + Stmt StmtNode + StmtType string } // ExecuteStmt is a statement to execute PreparedStmt. diff --git a/pkg/planner/core/plan_cache.go b/pkg/planner/core/plan_cache.go index 51117b0456..9f5c812389 100644 --- a/pkg/planner/core/plan_cache.go +++ b/pkg/planner/core/plan_cache.go @@ -95,22 +95,22 @@ func planCachePreprocess(ctx context.Context, sctx sessionctx.Context, isNonPrep vars.StmtCtx.StmtType = stmtAst.StmtType // step 1: check parameter number - if len(stmtAst.Params) != len(params) { + if len(stmt.Params) != len(params) { return errors.Trace(plannererrors.ErrWrongParamCount) } // step 2: set parameter values - if err := SetParameterValuesIntoSCtx(sctx.GetPlanCtx(), isNonPrepared, stmtAst.Params, params); err != nil { + if err := SetParameterValuesIntoSCtx(sctx.GetPlanCtx(), isNonPrepared, stmt.Params, params); err != nil { return errors.Trace(err) } // step 3: check schema version - if stmtAst.SchemaVersion != is.SchemaMetaVersion() { + if stmt.SchemaVersion != is.SchemaMetaVersion() { // In order to avoid some correctness issues, we have to clear the // cached plan once the schema version is changed. // Cached plan in prepared struct does NOT have a "cache key" with // schema version like prepared plan cache key - stmtAst.CachedPlan = nil + stmt.CachedPlan = nil stmt.Executor = nil stmt.ColumnInfos = nil // If the schema version has changed we need to preprocess it again, @@ -124,7 +124,7 @@ func planCachePreprocess(ctx context.Context, sctx sessionctx.Context, isNonPrep if err != nil { return plannererrors.ErrSchemaChanged.GenWithStack("Schema change caused error: %s", err.Error()) } - stmtAst.SchemaVersion = is.SchemaMetaVersion() + stmt.SchemaVersion = is.SchemaMetaVersion() } // step 4: handle expiration @@ -135,7 +135,7 @@ func planCachePreprocess(ctx context.Context, sctx sessionctx.Context, isNonPrep expiredTimeStamp4PC := domain.GetDomain(sctx).ExpiredTimeStamp4PC() if stmt.StmtCacheable && expiredTimeStamp4PC.Compare(vars.LastUpdateTime4PC) > 0 { sctx.GetSessionPlanCache().DeleteAll() - stmtAst.CachedPlan = nil + stmt.CachedPlan = nil vars.LastUpdateTime4PC = expiredTimeStamp4PC } return nil @@ -155,7 +155,6 @@ func GetPlanFromSessionPlanCache(ctx context.Context, sctx sessionctx.Context, var cacheKey kvcache.Key sessVars := sctx.GetSessionVars() stmtCtx := sessVars.StmtCtx - stmtAst := stmt.PreparedAst cacheEnabled := false if isNonPrepared { stmtCtx.CacheType = stmtctx.SessionNonPrepared @@ -190,13 +189,13 @@ func GetPlanFromSessionPlanCache(ctx context.Context, sctx sessionctx.Context, latestSchemaVersion = domain.GetDomain(sctx).InfoSchema().SchemaMetaVersion() } if cacheKey, err = NewPlanCacheKey(sctx.GetSessionVars(), stmt.StmtText, - stmt.StmtDB, stmtAst.SchemaVersion, latestSchemaVersion, bindSQL, expression.ExprPushDownBlackListReloadTimeStamp.Load()); err != nil { + stmt.StmtDB, stmt.SchemaVersion, latestSchemaVersion, bindSQL, expression.ExprPushDownBlackListReloadTimeStamp.Load()); err != nil { return nil, nil, err } } - if stmtCtx.UseCache && stmtAst.CachedPlan != nil { // special code path for fast point plan - if plan, names, ok, err := getCachedPointPlan(stmtAst, sessVars, stmtCtx); ok { + if stmtCtx.UseCache && stmt.CachedPlan != nil { // special code path for fast point plan + if plan, names, ok, err := getCachedPointPlan(stmt, sessVars, stmtCtx); ok { return plan, names, err } } @@ -234,7 +233,7 @@ func parseParamTypes(sctx sessionctx.Context, params []expression.Expression) (p return } -func getCachedPointPlan(stmt *ast.Prepared, sessVars *variable.SessionVars, stmtCtx *stmtctx.StatementContext) (Plan, +func getCachedPointPlan(stmt *PlanCacheStmt, sessVars *variable.SessionVars, stmtCtx *stmtctx.StatementContext) (Plan, []*types.FieldName, bool, error) { // short path for point-get plans // Rewriting the expression in the select.where condition will convert its @@ -335,7 +334,7 @@ func generateNewPlan(ctx context.Context, sctx sessionctx.Context, isNonPrepared if _, isolationReadContainTiFlash := sessVars.IsolationReadEngines[kv.TiFlash]; isolationReadContainTiFlash && !IsReadOnly(stmtAst.Stmt, sessVars) { delete(sessVars.IsolationReadEngines, kv.TiFlash) if cacheKey, err = NewPlanCacheKey(sessVars, stmt.StmtText, stmt.StmtDB, - stmtAst.SchemaVersion, latestSchemaVersion, bindSQL, expression.ExprPushDownBlackListReloadTimeStamp.Load()); err != nil { + stmt.SchemaVersion, latestSchemaVersion, bindSQL, expression.ExprPushDownBlackListReloadTimeStamp.Load()); err != nil { return nil, nil, err } sessVars.IsolationReadEngines[kv.TiFlash] = struct{}{} @@ -777,9 +776,8 @@ func tryCachePointPlan(_ context.Context, sctx PlanContext, return nil } var ( - stmtAst = stmt.PreparedAst - ok bool - err error + ok bool + err error ) if plan, _ok := p.(*PointGetPlan); _ok { @@ -794,8 +792,8 @@ func tryCachePointPlan(_ context.Context, sctx PlanContext, if ok { // just cache point plan now - stmtAst.CachedPlan = p - stmtAst.CachedNames = names + stmt.CachedPlan = p + stmt.CachedNames = names stmt.NormalizedPlan, stmt.PlanDigest = NormalizePlan(p) sctx.GetSessionVars().StmtCtx.SetPlan(p) sctx.GetSessionVars().StmtCtx.SetPlanDigest(stmt.NormalizedPlan, stmt.PlanDigest) @@ -807,16 +805,15 @@ func tryCachePointPlan(_ context.Context, sctx PlanContext, // Be careful with the short path, current precondition is ths cached plan satisfying // IsPointGetWithPKOrUniqueKeyByAutoCommit func IsPointGetPlanShortPathOK(sctx sessionctx.Context, is infoschema.InfoSchema, stmt *PlanCacheStmt) (bool, error) { - stmtAst := stmt.PreparedAst - if stmtAst.CachedPlan == nil || staleread.IsStmtStaleness(sctx) { + if stmt.CachedPlan == nil || staleread.IsStmtStaleness(sctx) { return false, nil } // check auto commit if !IsAutoCommitTxn(sctx.GetSessionVars()) { return false, nil } - if stmtAst.SchemaVersion != is.SchemaMetaVersion() { - stmtAst.CachedPlan = nil + if stmt.SchemaVersion != is.SchemaMetaVersion() { + stmt.CachedPlan = nil stmt.ColumnInfos = nil return false, nil } @@ -824,15 +821,15 @@ func IsPointGetPlanShortPathOK(sctx sessionctx.Context, is infoschema.InfoSchema // only point select/update will be cached, see "getPhysicalPlan" func var ok bool var err error - switch stmtAst.CachedPlan.(type) { + switch stmt.CachedPlan.(type) { case *PointGetPlan: ok = true case *Update: - pointUpdate := stmtAst.CachedPlan.(*Update) + pointUpdate := stmt.CachedPlan.(*Update) _, ok = pointUpdate.SelectPlan.(*PointGetPlan) if !ok { err = errors.Errorf("cached update plan not point update") - stmtAst.CachedPlan = nil + stmt.CachedPlan = nil return false, err } default: diff --git a/pkg/planner/core/plan_cache_utils.go b/pkg/planner/core/plan_cache_utils.go index 4990539cf8..ff1e68bf8d 100644 --- a/pkg/planner/core/plan_cache_utils.go +++ b/pkg/planner/core/plan_cache_utils.go @@ -121,10 +121,8 @@ func GeneratePlanCacheStmtWithAST(ctx context.Context, sctx sessionctx.Context, } prepared := &ast.Prepared{ - Stmt: paramStmt, - StmtType: ast.GetStmtLabel(paramStmt), - Params: extractor.markers, - SchemaVersion: ret.InfoSchema.SchemaMetaVersion(), + Stmt: paramStmt, + StmtType: ast.GetStmtLabel(paramStmt), } normalizedSQL, digest := parser.NormalizeDigest(prepared.Stmt.Text()) @@ -158,8 +156,8 @@ func GeneratePlanCacheStmtWithAST(ctx context.Context, sctx sessionctx.Context, // parameters are unknown here, so regard them all as NULL. // For non-prepared statements, all parameters are already initialized at `ParameterizeAST`, so no need to set NULL. if isPrepStmt { - for i := range prepared.Params { - param := prepared.Params[i].(*driver.ParamMarkerExpr) + for i := range extractor.markers { + param := extractor.markers[i].(*driver.ParamMarkerExpr) param.Datum.SetNull() param.InExecute = false } @@ -194,6 +192,8 @@ func GeneratePlanCacheStmtWithAST(ctx context.Context, sctx sessionctx.Context, StmtCacheable: cacheable, UncacheableReason: reason, QueryFeatures: features, + SchemaVersion: ret.InfoSchema.SchemaMetaVersion(), + Params: extractor.markers, } if err = CheckPreparedPriv(sctx, preparedObj, ret.InfoSchema); err != nil { return nil, nil, 0, err @@ -447,11 +447,17 @@ type PlanCacheStmt struct { StmtDB string // which DB the statement will be processed over VisitInfos []visitInfo ColumnInfos any + Params []ast.ParamMarkerExpr // Executor is only used for point get scene. // Notice that we should only cache the PointGetExecutor that have a snapshot with MaxTS in it. // If the current plan is not PointGet or does not use MaxTS optimization, this value should be nil here. Executor any + // below fields are for PointGet short path + SchemaVersion int64 + CachedPlan any + CachedNames any + StmtCacheable bool // Whether this stmt is cacheable. UncacheableReason string // Why this stmt is uncacheable. QueryFeatures *PlanCacheQueryFeatures diff --git a/pkg/server/driver_tidb.go b/pkg/server/driver_tidb.go index 749b274f58..653205ca73 100644 --- a/pkg/server/driver_tidb.go +++ b/pkg/server/driver_tidb.go @@ -203,7 +203,7 @@ func (ts *TiDBStatement) Close() error { } bindSQL, _ := bindinfo.MatchSQLBindingForPlanCache(ts.ctx, preparedObj.PreparedAst.Stmt, &preparedObj.BindingInfo) cacheKey, err := core.NewPlanCacheKey(ts.ctx.GetSessionVars(), preparedObj.StmtText, preparedObj.StmtDB, - preparedObj.PreparedAst.SchemaVersion, 0, bindSQL, expression.ExprPushDownBlackListReloadTimeStamp.Load()) + preparedObj.SchemaVersion, 0, bindSQL, expression.ExprPushDownBlackListReloadTimeStamp.Load()) if err != nil { return err } diff --git a/pkg/session/session.go b/pkg/session/session.go index aab2650234..59d9e5b6e4 100644 --- a/pkg/session/session.go +++ b/pkg/session/session.go @@ -298,17 +298,16 @@ func (s *session) cleanRetryInfo() { planCacheEnabled := s.GetSessionVars().EnablePreparedPlanCache var cacheKey kvcache.Key var err error - var preparedAst *ast.Prepared + var preparedObj *plannercore.PlanCacheStmt var stmtText, stmtDB string if planCacheEnabled { firstStmtID := retryInfo.DroppedPreparedStmtIDs[0] if preparedPointer, ok := s.sessionVars.PreparedStmts[firstStmtID]; ok { - preparedObj, ok := preparedPointer.(*plannercore.PlanCacheStmt) + preparedObj, ok = preparedPointer.(*plannercore.PlanCacheStmt) if ok { - preparedAst = preparedObj.PreparedAst stmtText, stmtDB = preparedObj.StmtText, preparedObj.StmtDB bindSQL, _ := bindinfo.MatchSQLBindingForPlanCache(s.pctx, preparedObj.PreparedAst.Stmt, &preparedObj.BindingInfo) - cacheKey, err = plannercore.NewPlanCacheKey(s.sessionVars, stmtText, stmtDB, preparedAst.SchemaVersion, + cacheKey, err = plannercore.NewPlanCacheKey(s.sessionVars, stmtText, stmtDB, preparedObj.SchemaVersion, 0, bindSQL, expression.ExprPushDownBlackListReloadTimeStamp.Load()) if err != nil { logutil.Logger(s.currentCtx).Warn("clean cached plan failed", zap.Error(err)) @@ -319,8 +318,8 @@ func (s *session) cleanRetryInfo() { } for i, stmtID := range retryInfo.DroppedPreparedStmtIDs { if planCacheEnabled { - if i > 0 && preparedAst != nil { - plannercore.SetPstmtIDSchemaVersion(cacheKey, stmtText, preparedAst.SchemaVersion, s.sessionVars.IsolationReadEngines) + if i > 0 && preparedObj != nil { + plannercore.SetPstmtIDSchemaVersion(cacheKey, stmtText, preparedObj.SchemaVersion, s.sessionVars.IsolationReadEngines) } if !s.sessionVars.IgnorePreparedCacheCloseStmt { // keep the plan in cache s.GetSessionPlanCache().Delete(cacheKey)