diff --git a/planner/core/plan_cache.go b/planner/core/plan_cache.go index 24f7af7cd115b..d8072875b5e71 100644 --- a/planner/core/plan_cache.go +++ b/planner/core/plan_cache.go @@ -126,14 +126,24 @@ func GetPlanFromSessionPlanCache(ctx context.Context, sctx sessionctx.Context, stmtCtx.UseCache = stmtAst.UseCache var bindSQL string - var ignorePlanCache = false + if stmtCtx.UseCache { + var ignoreByBinding bool + bindSQL, ignoreByBinding = GetBindSQL4PlanCache(sctx, stmt) + if ignoreByBinding { + stmtCtx.SetSkipPlanCache(errors.Errorf("skip plan-cache: ignore plan cache by binding")) + } + } // In rc or for update read, we need the latest schema version to decide whether we need to // rebuild the plan. So we set this value in rc or for update read. In other cases, let it be 0. var latestSchemaVersion int64 +<<<<<<< HEAD if stmtAst.UseCache { bindSQL, ignorePlanCache = GetBindSQL4PlanCache(sctx, stmt) +======= + if stmtCtx.UseCache { +>>>>>>> 7fafb6db45d (planner: better coordination between the ignore_plan_cache() binding and plan-cache (#40280)) if sctx.GetSessionVars().IsIsolation(ast.ReadCommitted) || stmt.ForUpdateRead { // In Rc or ForUpdateRead, we should check if the information schema has been changed since // last time. If it changed, we should rebuild the plan. Here, we use a different and more @@ -148,21 +158,35 @@ func GetPlanFromSessionPlanCache(ctx context.Context, sctx sessionctx.Context, paramNum, paramTypes := parseParamTypes(sctx, params) +<<<<<<< HEAD if stmtAst.UseCache && stmtAst.CachedPlan != nil && !ignorePlanCache { // for point query plan if plan, names, ok, err := getPointQueryPlan(stmtAst, sessVars, stmtCtx); ok { +======= + if stmtCtx.UseCache && stmtAst.CachedPlan != nil { // for point query plan + if plan, names, ok, err := getCachedPointPlan(stmtAst, sessVars, stmtCtx); ok { +>>>>>>> 7fafb6db45d (planner: better coordination between the ignore_plan_cache() binding and plan-cache (#40280)) return plan, names, err } } +<<<<<<< HEAD if stmtAst.UseCache && !ignorePlanCache { // for general plans if plan, names, ok, err := getGeneralPlan(sctx, isGeneralPlanCache, cacheKey, bindSQL, is, stmt, +======= + if stmtCtx.UseCache { // for non-point plans + if plan, names, ok, err := getCachedPlan(sctx, isNonPrepared, cacheKey, bindSQL, is, stmt, +>>>>>>> 7fafb6db45d (planner: better coordination between the ignore_plan_cache() binding and plan-cache (#40280)) paramTypes); err != nil || ok { return plan, names, err } } +<<<<<<< HEAD return generateNewPlan(ctx, sctx, isGeneralPlanCache, is, stmt, ignorePlanCache, cacheKey, latestSchemaVersion, paramNum, paramTypes, bindSQL) +======= + return generateNewPlan(ctx, sctx, isNonPrepared, is, stmt, cacheKey, latestSchemaVersion, paramNum, paramTypes, bindSQL) +>>>>>>> 7fafb6db45d (planner: better coordination between the ignore_plan_cache() binding and plan-cache (#40280)) } // parseParamTypes get parameters' types in PREPARE statement @@ -253,8 +277,12 @@ func getGeneralPlan(sctx sessionctx.Context, isGeneralPlanCache bool, cacheKey k // generateNewPlan call the optimizer to generate a new plan for current statement // and try to add it to cache +<<<<<<< HEAD func generateNewPlan(ctx context.Context, sctx sessionctx.Context, isGeneralPlanCache bool, is infoschema.InfoSchema, stmt *PlanCacheStmt, ignorePlanCache bool, cacheKey kvcache.Key, latestSchemaVersion int64, paramNum int, +======= +func generateNewPlan(ctx context.Context, sctx sessionctx.Context, isNonPrepared bool, is infoschema.InfoSchema, stmt *PlanCacheStmt, cacheKey kvcache.Key, latestSchemaVersion int64, paramNum int, +>>>>>>> 7fafb6db45d (planner: better coordination between the ignore_plan_cache() binding and plan-cache (#40280)) paramTypes []*types.FieldType, bindSQL string) (Plan, []*types.FieldName, error) { stmtAst := stmt.PreparedAst sessVars := sctx.GetSessionVars() @@ -276,7 +304,7 @@ func generateNewPlan(ctx context.Context, sctx sessionctx.Context, isGeneralPlan if containTableDual(p) && paramNum > 0 { stmtCtx.SetSkipPlanCache(errors.New("skip plan-cache: get a TableDual plan")) } - if stmtCtx.UseCache && !ignorePlanCache { + if stmtCtx.UseCache { // rebuild key to exclude kv.TiFlash when stmt is not read only if _, isolationReadContainTiFlash := sessVars.IsolationReadEngines[kv.TiFlash]; isolationReadContainTiFlash && !IsReadOnly(stmtAst.Stmt, sessVars) { delete(sessVars.IsolationReadEngines, kv.TiFlash) diff --git a/planner/core/plan_cache_test.go b/planner/core/plan_cache_test.go index c678209dbe1ec..17b41e43e3d5e 100644 --- a/planner/core/plan_cache_test.go +++ b/planner/core/plan_cache_test.go @@ -314,7 +314,37 @@ func TestPlanCacheDiagInfo(t *testing.T) { tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 skip plan-cache: some parameters may be overwritten")) } +<<<<<<< HEAD func TestIssue40224(t *testing.T) { +======= +func TestIssue40225(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (a int, key(a))") + tk.MustExec("prepare st from 'select * from t where a INT) since plan-cache is totally disabled. + + tk.MustExec("prepare st from 'select * from t where a>?'") + tk.MustExec("set @a=1") + tk.MustExec("execute st using @a") + tk.MustExec("execute st using @a") + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) + tk.MustExec("create binding for select * from t where a>1 using select /*+ ignore_plan_cache() */ * from t where a>1") + tk.MustExec("execute st using @a") + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) + tk.MustExec("execute st using @a") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) +} + +func TestUncacheableReason(t *testing.T) { +>>>>>>> 7fafb6db45d (planner: better coordination between the ignore_plan_cache() binding and plan-cache (#40280)) store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) tk.MustExec("use test")