diff --git a/executor/prepared_serial_test.go b/executor/prepared_serial_test.go index ae6f174bbd8c9..dde9ae973b7e9 100644 --- a/executor/prepared_serial_test.go +++ b/executor/prepared_serial_test.go @@ -706,11 +706,6 @@ func TestPlanCacheOperators(t *testing.T) { // execute this statement and check whether it uses a cached plan results := tk.MustQuery("execute stmt " + usingStmt).Sort().Rows() - useCache := "0" - if execCase.UseCache { - useCache = "1" - } - tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows(useCache)) // check whether the result is correct tmp := strings.Split(prepCase.PrepStmt, "?") diff --git a/planner/core/common_plans.go b/planner/core/common_plans.go index 13029e49847f4..10241797b6fe9 100644 --- a/planner/core/common_plans.go +++ b/planner/core/common_plans.go @@ -610,6 +610,9 @@ func (e *Execute) rebuildRange(p Plan) error { if err != nil { return err } + if len(ranges.Ranges) == 0 || len(ranges.AccessConds) != len(x.AccessConditions) { + return errors.New("failed to rebuild range: the length of the range has changed") + } for i := range x.IndexValues { x.IndexValues[i] = ranges.Ranges[0].LowVal[i] } @@ -625,6 +628,9 @@ func (e *Execute) rebuildRange(p Plan) error { if err != nil { return err } + if len(ranges) == 0 { + return errors.New("failed to rebuild range: the length of the range has changed") + } x.Handle = kv.IntHandle(ranges[0].LowVal[0].GetInt64()) } } @@ -658,6 +664,9 @@ func (e *Execute) rebuildRange(p Plan) error { if err != nil { return err } + if len(ranges.Ranges) != len(x.IndexValues) || len(ranges.AccessConds) != len(x.AccessConditions) { + return errors.New("failed to rebuild range: the length of the range has changed") + } for i := range x.IndexValues { for j := range ranges.Ranges[i].LowVal { x.IndexValues[i][j] = ranges.Ranges[i].LowVal[j] @@ -675,6 +684,9 @@ func (e *Execute) rebuildRange(p Plan) error { if err != nil { return err } + if len(ranges) != len(x.Handles) { + return errors.New("failed to rebuild range: the length of the range has changed") + } for i := range ranges { x.Handles[i] = kv.IntHandle(ranges[i].LowVal[0].GetInt64()) } diff --git a/planner/core/find_best_task.go b/planner/core/find_best_task.go index 3e13a6bb6aea1..e0abd33d197b7 100644 --- a/planner/core/find_best_task.go +++ b/planner/core/find_best_task.go @@ -803,7 +803,11 @@ func (ds *DataSource) findBestTask(prop *property.PhysicalProperty, planCounter continue } // if we already know the range of the scan is empty, just return a TableDual - if len(path.Ranges) == 0 && !ds.ctx.GetSessionVars().StmtCtx.UseCache { + if len(path.Ranges) == 0 { + // We should uncache the tableDual plan. + if expression.MaybeOverOptimized4PlanCache(ds.ctx, path.AccessConds) { + ds.ctx.GetSessionVars().StmtCtx.MaybeOverOptimized4PlanCache = true + } dual := PhysicalTableDual{}.Init(ds.ctx, ds.stats, ds.blockOffset) dual.SetSchema(ds.schema) cntPlan += 1 diff --git a/planner/core/prepare_test.go b/planner/core/prepare_test.go index 7981a326af8e8..d633896e7b718 100644 --- a/planner/core/prepare_test.go +++ b/planner/core/prepare_test.go @@ -1850,8 +1850,11 @@ func (s *testPrepareSerialSuite) TestIssue28246(c *C) { tk.MustExec("set @a=9223372036854775807, @b=1") tk.MustExec(`prepare stmt from 'select min(col1) from PK_AUTO_RANDOM9111 where col1 > ?;';`) tk.MustQuery("execute stmt using @a").Check(testkit.Rows("")) + // The plan contains the tableDual, so it will not be cached. + tk.MustQuery("execute stmt using @a").Check(testkit.Rows("")) + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) tk.MustQuery("execute stmt using @b").Check(testkit.Rows("9223372036854775807")) - tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) tk.MustQuery("execute stmt using @a").Check(testkit.Rows("")) tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) } @@ -1891,6 +1894,117 @@ func (s *testPrepareSerialSuite) TestIssue29805(c *C) { tk.MustQuery("select/*+ hash_agg() */ count(distinct col1) from PK_TCOLLATION10197 where col1 > '้พบ';").Check(testkit.Rows("0")) } +func (s *testPrepareSerialSuite) TestIssue29993(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() + err = store.Close() + c.Assert(err, IsNil) + 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") + + // test PointGet + cluster index + tk.MustExec("set tidb_enable_clustered_index=on;") + tk.MustExec("drop table if exists t;") + tk.MustExec("CREATE TABLE `t` (`COL1` enum('a', 'b') NOT NULL PRIMARY KEY, col2 int) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;") + tk.MustExec("insert into t values('a', 1), ('b', 2);") + tk.MustExec("set @a='a', @b='b', @z='z';") + tk.MustExec(`prepare stmt from 'select col1 from t where col1 = ? and col2 in (1, 2);';`) + tk.MustQuery("execute stmt using @a").Check(testkit.Rows("a")) + tk.MustQuery("execute stmt using @b").Check(testkit.Rows("b")) + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt using @z").Check(testkit.Rows()) + // The length of range have been changed, so the plan can not be cached. + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) + tk.MustQuery("execute stmt using @z").Check(testkit.Rows()) + + // test batchPointGet + cluster index + tk.MustExec("drop table if exists t;") + tk.MustExec("CREATE TABLE `t` (`COL1` enum('a', 'b') NOT NULL, col2 int, PRIMARY KEY(col1, col2)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;") + tk.MustExec("insert into t values('a', 1), ('b', 2);") + tk.MustExec("set @a='a', @b='b', @z='z';") + tk.MustExec(`prepare stmt from 'select col1 from t where (col1, col2) in ((?, 1));';`) + tk.MustQuery("execute stmt using @a").Check(testkit.Rows("a")) + tk.MustQuery("execute stmt using @b").Check(testkit.Rows()) + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt using @z").Check(testkit.Rows()) + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt using @z").Check(testkit.Rows()) + + // test PointGet + non cluster index + tk.MustExec("set tidb_enable_clustered_index=off;") + tk.MustExec("drop table if exists t;") + tk.MustExec("CREATE TABLE `t` (`COL1` enum('a', 'b') NOT NULL PRIMARY KEY, col2 int) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;") + tk.MustExec("insert into t values('a', 1), ('b', 2);") + tk.MustExec("set @a='a', @b='b', @z='z';") + tk.MustExec(`prepare stmt from 'select col1 from t where col1 = ? and col2 in (1, 2);';`) + tk.MustQuery("execute stmt using @a").Check(testkit.Rows("a")) + tk.MustQuery("execute stmt using @b").Check(testkit.Rows("b")) + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt using @z").Check(testkit.Rows()) + // The length of range have been changed, so the plan can not be cached. + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) + tk.MustQuery("execute stmt using @z").Check(testkit.Rows()) + + // test batchPointGet + non cluster index + tk.MustExec("drop table if exists t;") + tk.MustExec("CREATE TABLE `t` (`COL1` enum('a', 'b') NOT NULL, col2 int, PRIMARY KEY(col1, col2)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;") + tk.MustExec("insert into t values('a', 1), ('b', 2);") + tk.MustExec("set @a='a', @b='b', @z='z';") + tk.MustExec(`prepare stmt from 'select col1 from t where (col1, col2) in ((?, 1));';`) + tk.MustQuery("execute stmt using @a").Check(testkit.Rows("a")) + tk.MustQuery("execute stmt using @b").Check(testkit.Rows()) + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt using @z").Check(testkit.Rows()) + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt using @z").Check(testkit.Rows()) +} + +func (s *testPrepareSerialSuite) TestIssue30100(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() + err = store.Close() + c.Assert(err, IsNil) + 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(col1 enum('aa', 'bb'), col2 int, index(col1, col2));") + tk.MustExec("insert into t values('aa', 333);") + tk.MustExec(`prepare stmt from 'SELECT * FROM t t1 JOIN t t2 ON t1.col1 = t2.col1 WHERE t1.col1 <=> NULL';`) + tk.MustQuery("execute stmt").Check(testkit.Rows()) + tk.MustQuery("execute stmt").Check(testkit.Rows()) + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) + + tk.MustExec(`prepare stmt from 'SELECT * FROM t t1 JOIN t t2 ON t1.col1 = t2.col1 WHERE t1.col1 <=> NULL and t2.col2 > ?';`) + tk.MustExec("set @a=0;") + tk.MustQuery("execute stmt using @a").Check(testkit.Rows()) + tk.MustQuery("execute stmt using @a").Check(testkit.Rows()) + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) +} + func (s *testPlanSerialSuite) TestPartitionTable(c *C) { if israce.RaceEnabled { c.Skip("exhaustive types test, skip race test")