diff --git a/planner/core/plan_cache.go b/planner/core/plan_cache.go index e58e8b6d91708..bd1d3a4eaa84b 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 { +>>>>>>> 7fafb6db45 (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 { +>>>>>>> 7fafb6db45 (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, +>>>>>>> 7fafb6db45 (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) +>>>>>>> 7fafb6db45 (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, +>>>>>>> 7fafb6db45 (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,11 @@ func generateNewPlan(ctx context.Context, sctx sessionctx.Context, isGeneralPlan if containTableDual(p) && paramNum > 0 { stmtCtx.SkipPlanCache = true } +<<<<<<< HEAD if stmtAst.UseCache && !stmtCtx.SkipPlanCache && !ignorePlanCache { +======= + if stmtCtx.UseCache { +>>>>>>> 7fafb6db45 (planner: better coordination between the ignore_plan_cache() binding and plan-cache (#40280)) // 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 48ce6c9435c16..3285e40d97b3f 100644 --- a/planner/core/plan_cache_test.go +++ b/planner/core/plan_cache_test.go @@ -231,3 +231,48 @@ func TestPlanCacheDiagInfo(t *testing.T) { tk.MustExec("execute stmt using @a, @b") // a=1 and a=1 -> a=1 tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 skip plan-cache: some parameters may be overwritten")) } +<<<<<<< HEAD +======= + +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) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (a int)") + + tk.MustExec("prepare st from 'select * from t limit ?'") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 skip plan-cache: query has 'limit ?' is un-cacheable")) + + tk.MustExec("set @a=1") + tk.MustQuery("execute st using @a").Check(testkit.Rows()) + tk.MustExec("prepare st from 'select * from t limit ?'") + // show the corresponding un-cacheable reason at execute-stage as well + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 skip plan-cache: query has 'limit ?' is un-cacheable")) +} +>>>>>>> 7fafb6db45 (planner: better coordination between the ignore_plan_cache() binding and plan-cache (#40280))