diff --git a/executor/executor.go b/executor/executor.go index 46565eea3f..b3869c1a09 100644 --- a/executor/executor.go +++ b/executor/executor.go @@ -1695,6 +1695,8 @@ func ResetContextOfStmt(ctx sessionctx.Context, s ast.StmtNode) (err error) { vars.SysErrorCount = errCount vars.SysWarningCount = warnCount vars.StmtCtx = sc + vars.PrevFoundInPlanCache = vars.FoundInPlanCache + vars.FoundInPlanCache = false return } diff --git a/executor/set.go b/executor/set.go index 1be6a349cf..28e27e51f8 100644 --- a/executor/set.go +++ b/executor/set.go @@ -15,6 +15,7 @@ package executor import ( "context" + "fmt" "strings" "github.com/pingcap/errors" @@ -173,6 +174,10 @@ func (e *SetExecutor) setSysVariable(name string, v *expression.VarAssignment) e if name == variable.TxnIsolationOneShot && sessionVars.InTxn() { return errors.Trace(ErrCantChangeTxCharacteristics) } + if name == variable.TiDBFoundInPlanCache { + sessionVars.StmtCtx.AppendWarning(fmt.Errorf("Set operation for '%s' will not take effect", variable.TiDBFoundInPlanCache)) + return nil + } err = variable.SetSessionSystemVar(sessionVars, name, value) if err != nil { return err diff --git a/go.mod b/go.mod index 98506c1236..088e2a2079 100644 --- a/go.mod +++ b/go.mod @@ -38,7 +38,7 @@ require ( github.com/pingcap/goleveldb v0.0.0-20191226122134-f82aafb29989 github.com/pingcap/kvproto v0.0.0-20200409034505-a5af800ca2ef github.com/pingcap/log v0.0.0-20200117041106-d28c14d3b1cd - github.com/pingcap/parser v0.0.0-20200410065024-81f3db8e6095 + github.com/pingcap/parser v0.0.0-20200413043052-ef80f4de418c github.com/pingcap/pd/v4 v4.0.0-beta.1.0.20200305072537-61d9f9cc35d3 github.com/pingcap/sysutil v0.0.0-20200408114249-ed3bd6f7fdb1 github.com/pingcap/tidb-tools v4.0.0-beta.1.0.20200306084441-875bd09aa3d5+incompatible diff --git a/go.sum b/go.sum index 6e353f561d..68d249584e 100644 --- a/go.sum +++ b/go.sum @@ -269,8 +269,8 @@ github.com/pingcap/kvproto v0.0.0-20200409034505-a5af800ca2ef/go.mod h1:IOdRDPLy github.com/pingcap/log v0.0.0-20191012051959-b742a5d432e9/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= github.com/pingcap/log v0.0.0-20200117041106-d28c14d3b1cd h1:CV3VsP3Z02MVtdpTMfEgRJ4T9NGgGTxdHpJerent7rM= github.com/pingcap/log v0.0.0-20200117041106-d28c14d3b1cd/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= -github.com/pingcap/parser v0.0.0-20200410065024-81f3db8e6095 h1:DyL/YbS4r89FmiZd3XbUrpMSsVFtpOZzh1busGKytiI= -github.com/pingcap/parser v0.0.0-20200410065024-81f3db8e6095/go.mod h1:9v0Edh8IbgjGYW2ArJr19E+bvL8zKahsFp+ixWeId+4= +github.com/pingcap/parser v0.0.0-20200413043052-ef80f4de418c h1:8guNDWaL4d4Nsl+ptn2Z6ND/twrPtVYqy4+HxwSFPt8= +github.com/pingcap/parser v0.0.0-20200413043052-ef80f4de418c/go.mod h1:9v0Edh8IbgjGYW2ArJr19E+bvL8zKahsFp+ixWeId+4= github.com/pingcap/pd/v4 v4.0.0-beta.1.0.20200305072537-61d9f9cc35d3 h1:Yrp99FnjHAEuDrSBql2l0IqCtJX7KwJbTsD5hIArkvk= github.com/pingcap/pd/v4 v4.0.0-beta.1.0.20200305072537-61d9f9cc35d3/go.mod h1:25GfNw6+Jcr9kca5rtmTb4gKCJ4jOpow2zV2S9Dgafs= github.com/pingcap/sysutil v0.0.0-20200206130906-2bfa6dc40bcd/go.mod h1:EB/852NMQ+aRKioCpToQ94Wl7fktV+FNnxf3CX/TTXI= diff --git a/planner/core/common_plans.go b/planner/core/common_plans.go index de6b77b4e4..aa3ef5352e 100644 --- a/planner/core/common_plans.go +++ b/planner/core/common_plans.go @@ -31,6 +31,7 @@ import ( "github.com/pingcap/tidb/privilege" "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/sessionctx/stmtctx" + "github.com/pingcap/tidb/sessionctx/variable" "github.com/pingcap/tidb/table" "github.com/pingcap/tidb/types" driver "github.com/pingcap/tidb/types/parser_driver" @@ -263,6 +264,12 @@ func (e *Execute) checkPreparedPriv(ctx context.Context, sctx sessionctx.Context return err } +func (e *Execute) setFoundInPlanCache(sctx sessionctx.Context, opt bool) error { + vars := sctx.GetSessionVars() + err := vars.SetSystemVar(variable.TiDBFoundInPlanCache, variable.BoolToIntStr(opt)) + return err +} + func (e *Execute) getPhysicalPlan(ctx context.Context, sctx sessionctx.Context, is infoschema.InfoSchema, preparedStmt *CachedPrepareStmt) error { stmtCtx := sctx.GetSessionVars().StmtCtx prepared := preparedStmt.PreparedAst @@ -282,6 +289,10 @@ func (e *Execute) getPhysicalPlan(ctx context.Context, sctx sessionctx.Context, } else { planCacheCounter.Inc() } + err = e.setFoundInPlanCache(sctx, true) + if err != nil { + return err + } e.names = names e.Plan = plan stmtCtx.PointExec = true @@ -307,12 +318,16 @@ func (e *Execute) getPhysicalPlan(ctx context.Context, sctx sessionctx.Context, } } if planValid { + err := e.setFoundInPlanCache(sctx, true) + if err != nil { + return err + } if metrics.ResettablePlanCacheCounterFortTest { metrics.PlanCacheCounter.WithLabelValues("prepare").Inc() } else { planCacheCounter.Inc() } - err := e.rebuildRange(cachedVal.Plan) + err = e.rebuildRange(cachedVal.Plan) if err != nil { return err } @@ -336,10 +351,16 @@ func (e *Execute) getPhysicalPlan(ctx context.Context, sctx sessionctx.Context, isRange := e.isRangePartition(p) _, isTableDual := p.(*PhysicalTableDual) if !isTableDual && prepared.UseCache && !isRange { + err = e.setFoundInPlanCache(sctx, true) + if err != nil { + return err + } cached := NewPSTMTPlanCacheValue(p, names, stmtCtx.TblInfo2UnionScan) preparedStmt.NormalizedPlan, preparedStmt.PlanDigest = NormalizePlan(p) stmtCtx.SetPlanDigest(preparedStmt.NormalizedPlan, preparedStmt.PlanDigest) sctx.PreparedPlanCache().Put(cacheKey, cached) + } else { + err = e.setFoundInPlanCache(sctx, false) } return err } diff --git a/planner/core/prepare_test.go b/planner/core/prepare_test.go index 88eda813c5..037ebe1604 100644 --- a/planner/core/prepare_test.go +++ b/planner/core/prepare_test.go @@ -676,6 +676,46 @@ func (s *testPlanSerialSuite) TestPlanCacheUnionScan(c *C) { c.Check(cnt, Equals, float64(6)) } +func (s *testPlanSerialSuite) TestPlanCacheHitInfo(c *C) { + defer testleak.AfterTest(c)() + store, dom, err := newStoreWithBootstrap() + c.Assert(err, IsNil) + tk := testkit.NewTestKit(c, store) + orgEnable := core.PreparedPlanCacheEnabled() + defer func() { + dom.Close() + store.Close() + core.SetPreparedPlanCache(orgEnable) + }() + core.SetPreparedPlanCache(true) + + tk.Se, err = session.CreateSession4TestWithOpt(store, &session.Opt{ + PreparedPlanCache: kvcache.NewSimpleLRUCache(100, 0.1, math.MaxUint64), + }) + c.Assert(err, IsNil) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(id int)") + tk.MustExec("insert into t values (1),(2),(3),(4)") + tk.MustExec("prepare stmt from 'select * from t where id=?'") + tk.MustExec("prepare stmt2 from 'select /*+ ignore_plan_cache() */ * from t where id=?'") + tk.MustExec("set @doma = 1") + // Test if last_plan_from_cache is appropriately initialized. + tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0")) + tk.MustQuery("execute stmt using @doma").Check(testkit.Rows("1")) + // Test if last_plan_from_cache is updated after a plan cache hit. + tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1")) + tk.MustQuery("execute stmt2 using @doma").Check(testkit.Rows("1")) + // Test if last_plan_from_cache is updated after a plan cache miss caused by a prepared statement. + tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0")) + // Test if last_plan_from_cache is updated after a plan cache miss caused by a usual statement. + tk.MustQuery("execute stmt using @doma").Check(testkit.Rows("1")) + tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1")) + tk.MustQuery("select * from t where id=1").Check(testkit.Rows("1")) + tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0")) +} + func (s *testPrepareSuite) TestPrepareForGroupByMultiItems(c *C) { defer testleak.AfterTest(c)() store, dom, err := newStoreWithBootstrap() diff --git a/sessionctx/variable/session.go b/sessionctx/variable/session.go index cb74b1697c..32a7011735 100644 --- a/sessionctx/variable/session.go +++ b/sessionctx/variable/session.go @@ -594,6 +594,11 @@ type SessionVars struct { // WindowingUseHighPrecision determines whether to compute window operations without loss of precision. // see https://dev.mysql.com/doc/refman/8.0/en/window-function-optimization.html for more details. WindowingUseHighPrecision bool + + // FoundInPlanCache indicates whether this statement was found in plan cache. + FoundInPlanCache bool + // PrevFoundInPlanCache indicates whether the last statement was found in plan cache. + PrevFoundInPlanCache bool } // PreparedParams contains the parameters of the current prepared statement when executing it. @@ -679,6 +684,8 @@ func NewSessionVars() *SessionVars { MetricSchemaRangeDuration: DefTiDBMetricSchemaRangeDuration, SequenceState: NewSequenceState(), WindowingUseHighPrecision: true, + PrevFoundInPlanCache: DefTiDBFoundInPlanCache, + FoundInPlanCache: DefTiDBFoundInPlanCache, } vars.KVVars = kv.NewVariables(&vars.Killed) vars.Concurrency = Concurrency{ @@ -1247,6 +1254,8 @@ func (s *SessionVars) SetSystemVar(name string, val string) error { atomic.StoreUint64(&config.GetGlobalConfig().Log.QueryLogMaxLen, uint64(tidbOptInt64(val, logutil.DefaultQueryLogMaxLen))) case TiDBCheckMb4ValueInUTF8: config.GetGlobalConfig().CheckMb4ValueInUTF8 = TiDBOptOn(val) + case TiDBFoundInPlanCache: + s.FoundInPlanCache = TiDBOptOn(val) case TiDBEnableCollectExecutionInfo: config.GetGlobalConfig().EnableCollectExecutionInfo = TiDBOptOn(val) } diff --git a/sessionctx/variable/sysvar.go b/sessionctx/variable/sysvar.go index 217dcdf6b7..3aed4df066 100644 --- a/sessionctx/variable/sysvar.go +++ b/sessionctx/variable/sysvar.go @@ -718,6 +718,7 @@ var defaultSysVars = []*SysVar{ {ScopeSession, TiDBEnableSlowLog, BoolToIntStr(logutil.DefaultTiDBEnableSlowLog)}, {ScopeSession, TiDBQueryLogMaxLen, strconv.Itoa(logutil.DefaultQueryLogMaxLen)}, {ScopeSession, TiDBCheckMb4ValueInUTF8, BoolToIntStr(config.GetGlobalConfig().CheckMb4ValueInUTF8)}, + {ScopeSession, TiDBFoundInPlanCache, BoolToIntStr(DefTiDBFoundInPlanCache)}, {ScopeSession, TiDBEnableCollectExecutionInfo, BoolToIntStr(logutil.DefaultTiDBEnableSlowLog)}, } diff --git a/sessionctx/variable/tidb_vars.go b/sessionctx/variable/tidb_vars.go index d23322a431..0093336e29 100644 --- a/sessionctx/variable/tidb_vars.go +++ b/sessionctx/variable/tidb_vars.go @@ -169,6 +169,9 @@ const ( // TiDBCheckMb4ValueInUTF8 is used to control whether to enable the check wrong utf8 value. TiDBCheckMb4ValueInUTF8 = "tidb_check_mb4_value_in_utf8" + + // TiDBFoundInPlanCache indicates whether the last statement was found in plan cache + TiDBFoundInPlanCache = "last_plan_from_cache" ) // TiDB system variable names that both in session and global scope. @@ -484,6 +487,7 @@ const ( DefTiDBStoreLimit = 0 DefTiDBMetricSchemaStep = 60 // 60s DefTiDBMetricSchemaRangeDuration = 60 // 60s + DefTiDBFoundInPlanCache = false DefTidbEnableCollectExecutionInfo = false ) diff --git a/sessionctx/variable/varsutil.go b/sessionctx/variable/varsutil.go index 13d4843a49..2e08f7002b 100644 --- a/sessionctx/variable/varsutil.go +++ b/sessionctx/variable/varsutil.go @@ -152,6 +152,8 @@ func GetSessionOnlySysVars(s *SessionVars, key string) (string, bool, error) { return BoolToIntStr(config.GetGlobalConfig().CheckMb4ValueInUTF8), true, nil case TiDBCapturePlanBaseline: return CapturePlanBaseline.GetVal(), true, nil + case TiDBFoundInPlanCache: + return BoolToIntStr(s.PrevFoundInPlanCache), true, nil case TiDBEnableCollectExecutionInfo: return BoolToIntStr(config.GetGlobalConfig().EnableCollectExecutionInfo), true, nil } @@ -419,8 +421,8 @@ func ValidateSetSystemVar(vars *SessionVars, name string, value string) (string, TiDBBatchDelete, TiDBBatchCommit, TiDBEnableCascadesPlanner, TiDBEnableWindowFunction, TiDBPProfSQLCPU, TiDBLowResolutionTSO, TiDBEnableIndexMerge, TiDBEnableNoopFuncs, TiDBCheckMb4ValueInUTF8, TiDBEnableSlowLog, TiDBRecordPlanInSlowLog, - TiDBScatterRegion, TiDBGeneralLog, TiDBConstraintCheckInPlace, TiDBEnableVectorizedExpression, - TiDBEnableCollectExecutionInfo: + TiDBScatterRegion, TiDBGeneralLog, TiDBConstraintCheckInPlace, + TiDBEnableVectorizedExpression, TiDBFoundInPlanCache, TiDBEnableCollectExecutionInfo: fallthrough case GeneralLog, AvoidTemporalUpgrade, BigTables, CheckProxyUsers, LogBin, CoreFile, EndMakersInJSON, SQLLogBin, OfflineMode, PseudoSlaveMode, LowPriorityUpdates, diff --git a/sessionctx/variable/varsutil_test.go b/sessionctx/variable/varsutil_test.go index 6ecd775feb..073584bb4e 100644 --- a/sessionctx/variable/varsutil_test.go +++ b/sessionctx/variable/varsutil_test.go @@ -84,6 +84,7 @@ func (s *testVarsutilSuite) TestNewSessionVars(c *C) { c.Assert(vars.AllowWriteRowID, Equals, DefOptWriteRowID) c.Assert(vars.TiDBOptJoinReorderThreshold, Equals, DefTiDBOptJoinReorderThreshold) c.Assert(vars.EnableFastAnalyze, Equals, DefTiDBUseFastAnalyze) + c.Assert(vars.FoundInPlanCache, Equals, DefTiDBFoundInPlanCache) assertFieldsGreaterThanZero(c, reflect.ValueOf(vars.Concurrency)) assertFieldsGreaterThanZero(c, reflect.ValueOf(vars.MemQuota)) @@ -434,6 +435,13 @@ func (s *testVarsutilSuite) TestVarsutil(c *C) { c.Assert(val, Equals, "0") err = SetSessionSystemVar(v, TiDBStmtSummaryMaxSQLLength, types.NewStringDatum("a")) c.Assert(err, ErrorMatches, ".*Incorrect argument type to variable 'tidb_stmt_summary_max_sql_length'") + + err = SetSessionSystemVar(v, TiDBFoundInPlanCache, types.NewStringDatum("1")) + c.Assert(err, IsNil) + val, err = GetSessionSystemVar(v, TiDBFoundInPlanCache) + c.Assert(err, IsNil) + c.Assert(val, Equals, "0") + c.Assert(v.systems[TiDBFoundInPlanCache], Equals, "1") } func (s *testVarsutilSuite) TestSetOverflowBehave(c *C) {