From 207c89631cffbda50c83fbfd5b8260013a999fac Mon Sep 17 00:00:00 2001 From: Chengpeng Yan <41809508+Reminiscent@users.noreply.github.com> Date: Fri, 29 Oct 2021 18:09:49 +0800 Subject: [PATCH] cherry pick #28944 to release-5.2 Signed-off-by: ti-srebot --- executor/explainfor_test.go | 921 +++++++++++++++++++++++ executor/prepared_test.go | 2 +- executor/testdata/prepare_suite_out.json | 16 + expression/builtin_compare.go | 17 + expression/integration_test.go | 4 + expression/util.go | 43 ++ planner/core/expression_rewriter.go | 14 + planner/core/prepare_test.go | 135 ++++ 8 files changed, 1151 insertions(+), 1 deletion(-) diff --git a/executor/explainfor_test.go b/executor/explainfor_test.go index 89b9d934a4858..1aae0eded1724 100644 --- a/executor/explainfor_test.go +++ b/executor/explainfor_test.go @@ -216,6 +216,7 @@ type testPrepareSerialSuite struct { } func (s *testPrepareSerialSuite) TestExplainForConnPlanCache(c *C) { + c.Skip("unstable") if israce.RaceEnabled { c.Skip("skip race test") } @@ -243,8 +244,13 @@ func (s *testPrepareSerialSuite) TestExplainForConnPlanCache(c *C) { executeQuery := "execute stmt using @p0" explainQuery := "explain for connection " + strconv.FormatUint(tk1.Se.ShowProcess().ID, 10) explainResult := testkit.Rows( +<<<<<<< HEAD "TableReader_7 8000.00 root data:Selection_6", "└─Selection_6 8000.00 cop[tikv] eq(cast(test.t.a, double BINARY), 1)", +======= + "TableReader_7 10.00 root data:Selection_6", + "└─Selection_6 10.00 cop[tikv] eq(test.t.a, 1)", +>>>>>>> 83e559db0... planner: allow refineArgs for plan cache in some situations (#28944) " └─TableFullScan_5 10000.00 cop[tikv] table:t keep order:false, stats:pseudo", ) @@ -258,6 +264,9 @@ func (s *testPrepareSerialSuite) TestExplainForConnPlanCache(c *C) { PS: []*util.ProcessInfo{tk1.Se.ShowProcess()}, }) tk2.MustQuery(explainQuery).Check(explainResult) + tk1.MustExec(executeQuery) + // The plan can not be cached because the string type parameter will be convert to int type for calculation. + tk1.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) // multiple test, '1000' is both effective and efficient. repeats := 1000 @@ -525,3 +534,915 @@ func (s *testPrepareSerialSuite) TestExpressionIndexPreparePlanCache(c *C) { c.Assert(res.Rows()[2][3], Matches, ".*expression_index.*") c.Assert(res.Rows()[2][4], Matches, ".*[1234,1234].*") } +<<<<<<< HEAD +======= + +func (s *testPrepareSerialSuite) TestIssue28259(c *C) { + tk := testkit.NewTestKitWithInit(c, s.store) + + orgEnable := core.PreparedPlanCacheEnabled() + defer func() { + core.SetPreparedPlanCache(orgEnable) + }() + core.SetPreparedPlanCache(true) + + var err error + tk.Se, err = session.CreateSession4TestWithOpt(s.store, &session.Opt{ + PreparedPlanCache: kvcache.NewSimpleLRUCache(100, 0.1, math.MaxUint64), + }) + c.Assert(err, IsNil) + + // test for indexRange + tk.MustExec("use test") + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + tk.MustExec("drop table if exists UK_GCOL_VIRTUAL_18588;") + tk.MustExec("CREATE TABLE `UK_GCOL_VIRTUAL_18588` (`COL1` bigint(20), UNIQUE KEY `UK_COL1` (`COL1`)" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;") + tk.MustExec("insert into UK_GCOL_VIRTUAL_18588 values('8502658334322817163');") + tk.MustExec(`prepare stmt from 'select col1 from UK_GCOL_VIRTUAL_18588 where col1 between ? and ? or col1 < ?';`) + tk.MustExec("set @a=5516958330762833919, @b=8551969118506051323, @c=2887622822023883594;") + tk.MustQuery("execute stmt using @a,@b,@c;").Check(testkit.Rows("8502658334322817163")) + + tkProcess := tk.Se.ShowProcess() + ps := []*util.ProcessInfo{tkProcess} + tk.Se.SetSessionManager(&mockSessionManager1{PS: ps}) + res := tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) + c.Assert(len(res.Rows()), Equals, 3) + c.Assert(res.Rows()[0][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[2][0], Matches, ".*IndexRangeScan.*") + + tk.MustExec("set @a=-1696020282760139948, @b=-2619168038882941276, @c=-4004648990067362699;") + tk.MustQuery("execute stmt using @a,@b,@c;").Check(testkit.Rows()) + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt using @a,@b,@c;").Check(testkit.Rows()) + tkProcess = tk.Se.ShowProcess() + ps = []*util.ProcessInfo{tkProcess} + tk.Se.SetSessionManager(&mockSessionManager1{PS: ps}) + res = tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) + c.Assert(len(res.Rows()), Equals, 3) + c.Assert(res.Rows()[0][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[2][0], Matches, ".*IndexFullScan.*") + + res = tk.MustQuery("explain format = 'brief' select col1 from UK_GCOL_VIRTUAL_18588 use index(UK_COL1) " + + "where col1 between -1696020282760139948 and -2619168038882941276 or col1 < -4004648990067362699;") + c.Assert(len(res.Rows()), Equals, 3) + c.Assert(res.Rows()[1][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[2][0], Matches, ".*IndexFullScan.*") + res = tk.MustQuery("explain format = 'brief' select col1 from UK_GCOL_VIRTUAL_18588 use index(UK_COL1) " + + "where col1 between 5516958330762833919 and 8551969118506051323 or col1 < 2887622822023883594;") + c.Assert(len(res.Rows()), Equals, 2) + c.Assert(res.Rows()[1][0], Matches, ".*IndexRangeScan.*") + + tk.MustExec("drop table if exists t;") + tk.MustExec("CREATE TABLE t (a int, b int, index idx(a, b));") + tk.MustExec("insert into t values(1, 0);") + tk.MustExec(`prepare stmt from 'select a from t where (a between ? and ? or a < ?) and b < 1;'`) + tk.MustExec("set @a=0, @b=2, @c=2;") + tk.MustQuery("execute stmt using @a,@b,@c;").Check(testkit.Rows("1")) + tkProcess = tk.Se.ShowProcess() + ps = []*util.ProcessInfo{tkProcess} + tk.Se.SetSessionManager(&mockSessionManager1{PS: ps}) + res = tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) + c.Assert(len(res.Rows()), Equals, 5) + c.Assert(res.Rows()[1][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[1][4], Equals, "lt(test.t.b, 1), or(and(ge(test.t.a, 0), le(test.t.a, 2)), lt(test.t.a, 2))") + c.Assert(res.Rows()[2][0], Matches, ".*IndexReader.*") + c.Assert(res.Rows()[4][0], Matches, ".*IndexRangeScan.*") + + tk.MustExec("set @a=2, @b=1, @c=1;") + tk.MustQuery("execute stmt using @a,@b,@c;").Check(testkit.Rows()) + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt using @a,@b,@c;").Check(testkit.Rows()) + tkProcess = tk.Se.ShowProcess() + ps = []*util.ProcessInfo{tkProcess} + tk.Se.SetSessionManager(&mockSessionManager1{PS: ps}) + res = tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) + c.Assert(len(res.Rows()), Equals, 5) + c.Assert(res.Rows()[1][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[1][4], Equals, "lt(test.t.b, 1), or(and(ge(test.t.a, 2), le(test.t.a, 1)), lt(test.t.a, 1))") + c.Assert(res.Rows()[2][0], Matches, ".*IndexReader.*") + c.Assert(res.Rows()[4][0], Matches, ".*IndexFullScan.*") + + res = tk.MustQuery("explain format = 'brief' select a from t use index(idx) " + + "where (a between 0 and 2 or a < 2) and b < 1;") + c.Assert(len(res.Rows()), Equals, 4) + c.Assert(res.Rows()[1][0], Matches, ".*IndexReader.*") + c.Assert(res.Rows()[2][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[2][4], Equals, "lt(test.t.b, 1)") + c.Assert(res.Rows()[3][0], Matches, ".*IndexRangeScan.*") + res = tk.MustQuery("explain format = 'brief' select a from t use index(idx) " + + "where (a between 2 and 1 or a < 1) and b < 1;") + c.Assert(len(res.Rows()), Equals, 4) + c.Assert(res.Rows()[1][0], Matches, ".*IndexReader.*") + c.Assert(res.Rows()[2][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[2][4], Equals, "lt(test.t.b, 1)") + c.Assert(res.Rows()[3][0], Matches, ".*IndexRangeScan.*") + + // test for indexLookUp + tk.MustExec("drop table if exists t;") + tk.MustExec("CREATE TABLE t (a int, b int, index idx(a));") + tk.MustExec("insert into t values(1, 0);") + tk.MustExec(`prepare stmt from 'select /*+ USE_INDEX(t, idx) */ a from t where (a between ? and ? or a < ?) and b < 1;'`) + tk.MustExec("set @a=0, @b=2, @c=2;") + tk.MustQuery("execute stmt using @a,@b,@c;").Check(testkit.Rows("1")) + tkProcess = tk.Se.ShowProcess() + ps = []*util.ProcessInfo{tkProcess} + tk.Se.SetSessionManager(&mockSessionManager1{PS: ps}) + res = tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) + c.Assert(len(res.Rows()), Equals, 6) + c.Assert(res.Rows()[1][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[2][0], Matches, ".*IndexLookUp.*") + c.Assert(res.Rows()[3][0], Matches, ".*IndexRangeScan.*") + c.Assert(res.Rows()[4][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[5][0], Matches, ".*TableRowIDScan.*") + + tk.MustExec("set @a=2, @b=1, @c=1;") + tk.MustQuery("execute stmt using @a,@b,@c;").Check(testkit.Rows()) + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt using @a,@b,@c;").Check(testkit.Rows()) + tkProcess = tk.Se.ShowProcess() + ps = []*util.ProcessInfo{tkProcess} + tk.Se.SetSessionManager(&mockSessionManager1{PS: ps}) + res = tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) + c.Assert(len(res.Rows()), Equals, 6) + c.Assert(res.Rows()[1][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[2][0], Matches, ".*IndexLookUp.*") + c.Assert(res.Rows()[3][0], Matches, ".*IndexFullScan.*") + c.Assert(res.Rows()[4][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[5][0], Matches, ".*TableRowIDScan.*") + + res = tk.MustQuery("explain format = 'brief' select /*+ USE_INDEX(t, idx) */ a from t use index(idx) " + + "where (a between 0 and 2 or a < 2) and b < 1;") + c.Assert(len(res.Rows()), Equals, 5) + c.Assert(res.Rows()[1][0], Matches, ".*IndexLookUp.*") + c.Assert(res.Rows()[2][0], Matches, ".*IndexRangeScan.*") + res = tk.MustQuery("explain format = 'brief' select /*+ USE_INDEX(t, idx) */ a from t use index(idx) " + + "where (a between 2 and 1 or a < 1) and b < 1;") + c.Assert(len(res.Rows()), Equals, 5) + c.Assert(res.Rows()[1][0], Matches, ".*IndexLookUp.*") + c.Assert(res.Rows()[2][0], Matches, ".*IndexRangeScan.*") + + // test for tableReader + tk.MustExec("drop table if exists t;") + tk.MustExec("CREATE TABLE t (a int PRIMARY KEY CLUSTERED, b int);") + tk.MustExec("insert into t values(1, 0);") + tk.MustExec(`prepare stmt from 'select a from t where (a between ? and ? or a < ?) and b < 1;'`) + tk.MustExec("set @a=0, @b=2, @c=2;") + tk.MustQuery("execute stmt using @a,@b,@c;").Check(testkit.Rows("1")) + tkProcess = tk.Se.ShowProcess() + ps = []*util.ProcessInfo{tkProcess} + tk.Se.SetSessionManager(&mockSessionManager1{PS: ps}) + res = tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) + c.Assert(len(res.Rows()), Equals, 5) + c.Assert(res.Rows()[1][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[1][4], Equals, "lt(test.t.b, 1), or(and(ge(test.t.a, 0), le(test.t.a, 2)), lt(test.t.a, 2))") + c.Assert(res.Rows()[2][0], Matches, ".*TableReader.*") + c.Assert(res.Rows()[3][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[4][0], Matches, ".*TableRangeScan.*") + + tk.MustExec("set @a=2, @b=1, @c=1;") + tk.MustQuery("execute stmt using @a,@b,@c;").Check(testkit.Rows()) + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt using @a,@b,@c;").Check(testkit.Rows()) + tkProcess = tk.Se.ShowProcess() + ps = []*util.ProcessInfo{tkProcess} + tk.Se.SetSessionManager(&mockSessionManager1{PS: ps}) + res = tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) + c.Assert(len(res.Rows()), Equals, 5) + c.Assert(res.Rows()[1][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[1][4], Equals, "lt(test.t.b, 1), or(and(ge(test.t.a, 2), le(test.t.a, 1)), lt(test.t.a, 1))") + c.Assert(res.Rows()[2][0], Matches, ".*TableReader.*") + c.Assert(res.Rows()[3][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[4][0], Matches, ".*TableRangeScan.*") + + res = tk.MustQuery("explain format = 'brief' select a from t " + + "where (a between 0 and 2 or a < 2) and b < 1;") + c.Assert(len(res.Rows()), Equals, 4) + c.Assert(res.Rows()[1][0], Matches, ".*TableReader.*") + c.Assert(res.Rows()[2][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[2][4], Equals, "lt(test.t.b, 1)") + c.Assert(res.Rows()[3][0], Matches, ".*TableRangeScan.*") + res = tk.MustQuery("explain format = 'brief' select a from t " + + "where (a between 2 and 1 or a < 1) and b < 1;") + c.Assert(len(res.Rows()), Equals, 4) + c.Assert(res.Rows()[1][0], Matches, ".*TableReader.*") + c.Assert(res.Rows()[2][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[2][4], Equals, "lt(test.t.b, 1)") + c.Assert(res.Rows()[3][0], Matches, ".*TableRangeScan.*") + + tk.MustExec("drop table if exists t;") + tk.MustExec("CREATE TABLE t (a int primary key, b int, c int, d int);") + tk.MustExec(`prepare stmt from 'select * from t where ((a > ? and a < 5 and b > 2) or (a > ? and a < 10 and c > 3)) and d = 5;';`) + tk.MustExec("set @a=1, @b=8;") + tk.MustQuery("execute stmt using @a,@b;").Check(testkit.Rows()) + tkProcess = tk.Se.ShowProcess() + ps = []*util.ProcessInfo{tkProcess} + tk.Se.SetSessionManager(&mockSessionManager1{PS: ps}) + res = tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) + c.Assert(len(res.Rows()), Equals, 4) + c.Assert(res.Rows()[0][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[1][0], Matches, ".*TableReader.*") + c.Assert(res.Rows()[2][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[3][0], Matches, ".*TableRangeScan.*") +} + +func (s *testPrepareSerialSuite) TestIssue28696(c *C) { + tk := testkit.NewTestKitWithInit(c, s.store) + + orgEnable := core.PreparedPlanCacheEnabled() + defer func() { + core.SetPreparedPlanCache(orgEnable) + }() + core.SetPreparedPlanCache(true) + + var err error + tk.Se, err = session.CreateSession4TestWithOpt(s.store, &session.Opt{ + PreparedPlanCache: kvcache.NewSimpleLRUCache(100, 0.1, math.MaxUint64), + }) + c.Assert(err, IsNil) + + tk.MustExec("use test") + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + tk.MustExec("drop table if exists t1;") + tk.MustExec("create table t1(a int primary key, b varchar(255), c int);") + tk.MustExec("create unique index b on t1(b(3));") + tk.MustExec("insert into t1 values(1,'abcdfsafd',1),(2,'addfdsafd',2),(3,'ddcdsaf',3),(4,'bbcsa',4);") + tk.MustExec(`prepare stmt from "select a from t1 where b = ?";`) + tk.MustExec("set @a='bbcsa';") + tk.MustQuery("execute stmt using @a;").Check(testkit.Rows("4")) + + tkProcess := tk.Se.ShowProcess() + ps := []*util.ProcessInfo{tkProcess} + tk.Se.SetSessionManager(&mockSessionManager1{PS: ps}) + res := tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) + c.Assert(len(res.Rows()), Equals, 6) + c.Assert(res.Rows()[1][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[2][0], Matches, ".*IndexLookUp.*") + c.Assert(res.Rows()[3][0], Matches, ".*IndexRangeScan.*") + c.Assert(res.Rows()[4][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[5][0], Matches, ".*TableRowIDScan.*") + + res = tk.MustQuery("explain format = 'brief' select a from t1 where b = 'bbcsa';") + c.Assert(len(res.Rows()), Equals, 5) + c.Assert(res.Rows()[1][0], Matches, ".*IndexLookUp.*") + c.Assert(res.Rows()[2][0], Matches, ".*IndexRangeScan.*") + c.Assert(res.Rows()[3][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[4][0], Matches, ".*TableRowIDScan.*") +} + +func (s *testPrepareSerialSuite) TestIndexMerge4PlanCache(c *C) { + tk := testkit.NewTestKitWithInit(c, s.store) + + orgEnable := core.PreparedPlanCacheEnabled() + defer func() { + core.SetPreparedPlanCache(orgEnable) + }() + core.SetPreparedPlanCache(true) + + var err error + tk.Se, err = session.CreateSession4TestWithOpt(s.store, &session.Opt{ + PreparedPlanCache: kvcache.NewSimpleLRUCache(100, 0.1, math.MaxUint64), + }) + c.Assert(err, IsNil) + + tk.MustExec("use test") + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + tk.MustExec("drop table if exists IDT_MULTI15858STROBJSTROBJ;") + tk.MustExec("CREATE TABLE `IDT_MULTI15858STROBJSTROBJ` (" + + "`COL1` enum('aa','bb','cc','dd','ee','ff','gg','hh','ii','mm') DEFAULT NULL," + + "`COL2` int(41) DEFAULT NULL," + + "`COL3` datetime DEFAULT NULL," + + "KEY `U_M_COL4` (`COL1`,`COL2`)," + + "KEY `U_M_COL5` (`COL3`,`COL2`)" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;") + tk.MustExec("insert into IDT_MULTI15858STROBJSTROBJ values('aa', 1333053589,'1037-12-26 01:38:52');") + + tk.MustExec("set tidb_enable_index_merge=on;") + tk.MustExec("prepare stmt from 'select * from IDT_MULTI15858STROBJSTROBJ where col2 <> ? and col1 not in (?, ?, ?) or col3 = ? order by 2;';") + tk.MustExec("set @a=2134549621, @b='aa', @c='aa', @d='aa', @e='9941-07-07 01:08:48';") + tk.MustQuery("execute stmt using @a,@b,@c,@d,@e;").Check(testkit.Rows()) + + tk.MustExec("set @a=-2144294194, @b='mm', @c='mm', @d='mm', @e='0198-09-29 20:19:49';") + tk.MustQuery("execute stmt using @a,@b,@c,@d,@e;").Check(testkit.Rows("aa 1333053589 1037-12-26 01:38:52")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt using @a,@b,@c,@d,@e;").Check(testkit.Rows("aa 1333053589 1037-12-26 01:38:52")) + + tkProcess := tk.Se.ShowProcess() + ps := []*util.ProcessInfo{tkProcess} + tk.Se.SetSessionManager(&mockSessionManager1{PS: ps}) + res := tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) + c.Assert(len(res.Rows()), Equals, 7) + c.Assert(res.Rows()[1][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[2][0], Matches, ".*IndexMerge.*") + c.Assert(res.Rows()[4][0], Matches, ".*IndexRangeScan.*") + c.Assert(res.Rows()[4][4], Equals, "range:(NULL,\"mm\"), (\"mm\",+inf], keep order:false, stats:pseudo") + c.Assert(res.Rows()[5][0], Matches, ".*IndexRangeScan.*") + c.Assert(res.Rows()[5][4], Equals, "range:[0198-09-29 20:19:49,0198-09-29 20:19:49], keep order:false, stats:pseudo") + + // test for cluster index in indexMerge + tk.MustExec("drop table if exists t;") + tk.MustExec("set @@tidb_enable_clustered_index = 1;") + tk.MustExec("create table t(a int, b int, c int, primary key(a), index idx_b(b));") + tk.MustExec("prepare stmt from 'select * from t where ((a > ? and a < ?) or b > 1) and c > 1;';") + tk.MustExec("set @a = 0, @b = 3;") + tk.MustQuery("execute stmt using @a, @b;").Check(testkit.Rows()) + + tkProcess = tk.Se.ShowProcess() + ps = []*util.ProcessInfo{tkProcess} + tk.Se.SetSessionManager(&mockSessionManager1{PS: ps}) + res = tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) + c.Assert(len(res.Rows()), Equals, 6) + c.Assert(res.Rows()[0][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[1][0], Matches, ".*IndexMerge.*") + c.Assert(res.Rows()[2][0], Matches, ".*TableRangeScan.*") + c.Assert(res.Rows()[2][4], Equals, "range:(0,3), keep order:false, stats:pseudo") + c.Assert(res.Rows()[3][0], Matches, ".*IndexRangeScan.*") + c.Assert(res.Rows()[3][4], Equals, "range:(1,+inf], keep order:false, stats:pseudo") + + // test for prefix index + tk.MustExec("drop table if exists t1;") + tk.MustExec("create table t1(a int primary key, b varchar(255), c int, index idx_c(c));") + tk.MustExec("create unique index idx_b on t1(b(3));") + tk.MustExec("insert into t1 values(1,'abcdfsafd',1),(2,'addfdsafd',2),(3,'ddcdsaf',3),(4,'bbcsa',4);") + tk.MustExec("prepare stmt from 'select /*+ USE_INDEX_MERGE(t1, primary, idx_b, idx_c) */ * from t1 where b = ? or a > 10 or c > 10;';") + tk.MustExec("set @a='bbcsa', @b='ddcdsaf';") + tk.MustQuery("execute stmt using @a;").Check(testkit.Rows("4 bbcsa 4")) + + tkProcess = tk.Se.ShowProcess() + ps = []*util.ProcessInfo{tkProcess} + tk.Se.SetSessionManager(&mockSessionManager1{PS: ps}) + res = tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) + c.Assert(res.Rows()[0][0], Matches, ".*IndexMerge.*") + + tk.MustQuery("execute stmt using @b;").Check(testkit.Rows("3 ddcdsaf 3")) + // TODO: should use plan cache here + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + tk.MustQuery("execute stmt using @b;").Check(testkit.Rows("3 ddcdsaf 3")) + tkProcess = tk.Se.ShowProcess() + ps = []*util.ProcessInfo{tkProcess} + tk.Se.SetSessionManager(&mockSessionManager1{PS: ps}) + res = tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) + c.Assert(res.Rows()[0][0], Matches, ".*IndexMerge.*") + + // rewrite the origin indexMerge test + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t (a int, b int, c int, primary key(a), key(b))") + tk.MustExec("prepare stmt from 'select /*+ inl_join(t2) */ * from t t1 join t t2 on t1.a = t2.a and t1.c = t2.c where t2.a = 1 or t2.b = 1;';") + tk.MustQuery("execute stmt;").Check(testkit.Rows()) + + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1(a int primary key, b int, c int, key(b), key(c));") + tk.MustExec("INSERT INTO t1 VALUES (10, 10, 10), (11, 11, 11)") + tk.MustExec("prepare stmt from 'select /*+ use_index_merge(t1) */ * from t1 where c=? or (b=? and a=?);';") + tk.MustExec("set @a = 10, @b = 11;") + tk.MustQuery("execute stmt using @a, @a, @a").Check(testkit.Rows("10 10 10")) + tk.MustQuery("execute stmt using @b, @b, @b").Check(testkit.Rows("11 11 11")) + // TODO: should use plan cache here + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + + tk.MustExec("drop table if exists t0") + tk.MustExec("CREATE TABLE t0(c0 INT AS (1), c1 INT PRIMARY KEY)") + tk.MustExec("INSERT INTO t0(c1) VALUES (0)") + tk.MustExec("CREATE INDEX i0 ON t0(c0)") + tk.MustExec("prepare stmt from 'SELECT /*+ USE_INDEX_MERGE(t0, i0, PRIMARY)*/ t0.c0 FROM t0 WHERE t0.c1 OR t0.c0;';") + tk.MustQuery("execute stmt;").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt;").Check(testkit.Rows("1")) + // The plan contains the generated column, so it can not be cached. + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1(id int primary key, a int, b int, c int, d int)") + tk.MustExec("create index t1a on t1(a)") + tk.MustExec("create index t1b on t1(b)") + tk.MustExec("create table t2(id int primary key, a int)") + tk.MustExec("create index t2a on t2(a)") + tk.MustExec("insert into t1 values(1,1,1,1,1),(2,2,2,2,2),(3,3,3,3,3),(4,4,4,4,4),(5,5,5,5,5)") + tk.MustExec("insert into t2 values(1,1),(5,5)") + tk.MustExec("prepare stmt from 'select /*+ use_index_merge(t1, t1a, t1b) */ sum(t1.a) from t1 join t2 on t1.id = t2.id where t1.a < ? or t1.b > ?';") + tk.MustExec("set @a=2, @b=4, @c=5;") + tk.MustQuery("execute stmt using @a, @b").Check(testkit.Rows("6")) + tk.MustQuery("execute stmt using @a, @c").Check(testkit.Rows("1")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) +} + +func (s *testPrepareSerialSuite) TestSetOperations4PlanCache(c *C) { + tk := testkit.NewTestKitWithInit(c, s.store) + + orgEnable := core.PreparedPlanCacheEnabled() + defer func() { + core.SetPreparedPlanCache(orgEnable) + }() + core.SetPreparedPlanCache(true) + + var err error + tk.Se, err = session.CreateSession4TestWithOpt(s.store, &session.Opt{ + PreparedPlanCache: kvcache.NewSimpleLRUCache(100, 0.1, math.MaxUint64), + }) + c.Assert(err, IsNil) + + tk.MustExec("use test") + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + tk.MustExec("drop table if exists t1, t2;") + tk.MustExec("CREATE TABLE `t1` (a int);") + tk.MustExec("CREATE TABLE `t2` (a int);") + tk.MustExec("insert into t1 values(1), (2);") + tk.MustExec("insert into t2 values(1), (3);") + // test for UNION + tk.MustExec("prepare stmt from 'select * from t1 where a > ? union select * from t2 where a > ?;';") + tk.MustExec("set @a=0, @b=1;") + tk.MustQuery("execute stmt using @a, @b;").Sort().Check(testkit.Rows("1", "2", "3")) + tk.MustQuery("execute stmt using @b, @a;").Sort().Check(testkit.Rows("1", "2", "3")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt using @b, @b;").Sort().Check(testkit.Rows("2", "3")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt using @a, @a;").Sort().Check(testkit.Rows("1", "2", "3")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + + tk.MustExec("prepare stmt from 'select * from t1 where a > ? union all select * from t2 where a > ?;';") + tk.MustExec("set @a=0, @b=1;") + tk.MustQuery("execute stmt using @a, @b;").Sort().Check(testkit.Rows("1", "2", "3")) + tk.MustQuery("execute stmt using @b, @a;").Sort().Check(testkit.Rows("1", "2", "3")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt using @b, @b;").Sort().Check(testkit.Rows("2", "3")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt using @a, @a;").Sort().Check(testkit.Rows("1", "1", "2", "3")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + + // test for EXCEPT + tk.MustExec("prepare stmt from 'select * from t1 where a > ? except select * from t2 where a > ?;';") + tk.MustExec("set @a=0, @b=1;") + tk.MustQuery("execute stmt using @a, @a;").Sort().Check(testkit.Rows("2")) + tk.MustQuery("execute stmt using @b, @a;").Sort().Check(testkit.Rows("2")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt using @b, @b;").Sort().Check(testkit.Rows("2")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt using @a, @b;").Sort().Check(testkit.Rows("1", "2")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + + // test for INTERSECT + tk.MustExec("prepare stmt from 'select * from t1 where a > ? union select * from t2 where a > ?;';") + tk.MustExec("set @a=0, @b=1;") + tk.MustQuery("execute stmt using @a, @a;").Sort().Check(testkit.Rows("1", "2", "3")) + tk.MustQuery("execute stmt using @b, @a;").Sort().Check(testkit.Rows("1", "2", "3")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt using @b, @b;").Sort().Check(testkit.Rows("2", "3")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt using @a, @b;").Sort().Check(testkit.Rows("1", "2", "3")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + + // test for UNION + INTERSECT + tk.MustExec("prepare stmt from 'select * from t1 union all select * from t1 intersect select * from t2;'") + tk.MustQuery("execute stmt;").Sort().Check(testkit.Rows("1", "1", "2")) + + tk.MustExec("prepare stmt from '(select * from t1 union all select * from t1) intersect select * from t2;'") + tk.MustQuery("execute stmt;").Sort().Check(testkit.Rows("1")) + + // test for order by and limit + tk.MustExec("prepare stmt from '(select * from t1 union all select * from t1 intersect select * from t2) order by a limit 2;'") + tk.MustQuery("execute stmt;").Sort().Check(testkit.Rows("1", "1")) +} + +func (s *testPrepareSerialSuite) TestSPM4PlanCache(c *C) { + tk := testkit.NewTestKitWithInit(c, s.store) + + orgEnable := core.PreparedPlanCacheEnabled() + defer func() { + core.SetPreparedPlanCache(orgEnable) + }() + core.SetPreparedPlanCache(true) + + var err error + tk.Se, err = session.CreateSession4TestWithOpt(s.store, &session.Opt{ + PreparedPlanCache: kvcache.NewSimpleLRUCache(100, 0.1, math.MaxUint64), + }) + c.Assert(err, IsNil) + + tk.MustExec("use test") + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a int, index idx_a(a));") + tk.MustExec("delete from mysql.bind_info where default_db='test';") + tk.MustExec("admin reload bindings;") + + res := tk.MustQuery("explain format = 'brief' select * from t;") + c.Assert(res.Rows()[0][0], Matches, ".*TableReader.*") + c.Assert(res.Rows()[1][0], Matches, ".*TableFullScan.*") + + tk.MustExec("prepare stmt from 'select * from t;';") + tk.MustQuery("execute stmt;").Check(testkit.Rows()) + + tkProcess := tk.Se.ShowProcess() + ps := []*util.ProcessInfo{tkProcess} + tk.Se.SetSessionManager(&mockSessionManager1{PS: ps}) + res = tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) + c.Assert(res.Rows()[0][0], Matches, ".*TableReader.*") + c.Assert(res.Rows()[1][0], Matches, ".*TableFullScan.*") + + tk.MustExec("create global binding for select * from t using select * from t use index(idx_a);") + + res = tk.MustQuery("explain format = 'brief' select * from t;") + c.Assert(res.Rows()[0][0], Matches, ".*IndexReader.*") + c.Assert(res.Rows()[1][0], Matches, ".*IndexFullScan.*") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) + + tk.MustQuery("execute stmt;").Check(testkit.Rows()) + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt;").Check(testkit.Rows()) + tkProcess = tk.Se.ShowProcess() + ps = []*util.ProcessInfo{tkProcess} + tk.Se.SetSessionManager(&mockSessionManager1{PS: ps}) + res = tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) + // The binding does not take effect for caches that have been cached. + c.Assert(res.Rows()[0][0], Matches, ".*TableReader.*") + c.Assert(res.Rows()[1][0], Matches, ".*TableFullScan.*") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) + + tk.MustExec("delete from mysql.bind_info where default_db='test';") + tk.MustExec("admin reload bindings;") +} + +func (s *testPrepareSerialSuite) TestHint4PlanCache(c *C) { + tk := testkit.NewTestKitWithInit(c, s.store) + + orgEnable := core.PreparedPlanCacheEnabled() + defer func() { + core.SetPreparedPlanCache(orgEnable) + }() + core.SetPreparedPlanCache(true) + + var err error + tk.Se, err = session.CreateSession4TestWithOpt(s.store, &session.Opt{ + PreparedPlanCache: kvcache.NewSimpleLRUCache(100, 0.1, math.MaxUint64), + }) + c.Assert(err, IsNil) + + tk.MustExec("use test") + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a int, index idx_a(a));") + + tk.MustExec("prepare stmt from 'select * from t;';") + 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 /*+ IGNORE_PLAN_CACHE() */ * from t;';") + tk.MustQuery("execute stmt;").Check(testkit.Rows()) + tk.MustQuery("execute stmt;").Check(testkit.Rows()) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) +} + +func (s *testPrepareSerialSuite) TestSelectView4PlanCache(c *C) { + tk := testkit.NewTestKitWithInit(c, s.store) + + orgEnable := core.PreparedPlanCacheEnabled() + defer func() { + core.SetPreparedPlanCache(orgEnable) + }() + core.SetPreparedPlanCache(true) + + var err error + tk.Se, err = session.CreateSession4TestWithOpt(s.store, &session.Opt{ + PreparedPlanCache: kvcache.NewSimpleLRUCache(100, 0.1, math.MaxUint64), + }) + c.Assert(err, IsNil) + + tk.MustExec("use test") + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + tk.MustExec("drop table if exists view_t;") + tk.MustExec("create table view_t (a int,b int)") + tk.MustExec("insert into view_t values(1,2)") + tk.MustExec("create definer='root'@'localhost' view view1 as select * from view_t") + tk.MustExec("create definer='root'@'localhost' view view2(c,d) as select * from view_t") + tk.MustExec("create definer='root'@'localhost' view view3(c,d) as select a,b from view_t") + tk.MustExec("create definer='root'@'localhost' view view4 as select * from (select * from (select * from view_t) tb1) tb;") + tk.MustExec("prepare stmt1 from 'select * from view1;'") + tk.MustQuery("execute stmt1;").Check(testkit.Rows("1 2")) + tk.MustQuery("execute stmt1;").Check(testkit.Rows("1 2")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + + tk.MustExec("prepare stmt2 from 'select * from view2;'") + tk.MustQuery("execute stmt2;").Check(testkit.Rows("1 2")) + tk.MustQuery("execute stmt2;").Check(testkit.Rows("1 2")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + + tk.MustExec("prepare stmt3 from 'select * from view3;'") + tk.MustQuery("execute stmt3;").Check(testkit.Rows("1 2")) + tk.MustQuery("execute stmt3;").Check(testkit.Rows("1 2")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + + tk.MustExec("prepare stmt4 from 'select * from view4;'") + tk.MustQuery("execute stmt4;").Check(testkit.Rows("1 2")) + tk.MustQuery("execute stmt4;").Check(testkit.Rows("1 2")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + + tk.MustExec("drop table view_t;") + tk.MustExec("create table view_t(c int,d int)") + err = tk.ExecToErr("execute stmt1;") + c.Assert(err.Error(), Equals, "[planner:1356]View 'test.view1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them") + err = tk.ExecToErr("execute stmt2") + c.Assert(err.Error(), Equals, "[planner:1356]View 'test.view2' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them") + err = tk.ExecToErr("execute stmt3") + c.Assert(err.Error(), Equals, core.ErrViewInvalid.GenWithStackByArgs("test", "view3").Error()) + tk.MustExec("drop table view_t;") + tk.MustExec("create table view_t(a int,b int,c int)") + tk.MustExec("insert into view_t values(1,2,3)") + + tk.MustQuery("execute stmt1;").Check(testkit.Rows("1 2")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + tk.MustQuery("execute stmt1;").Check(testkit.Rows("1 2")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + + tk.MustQuery("execute stmt2;").Check(testkit.Rows("1 2")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + tk.MustQuery("execute stmt2;").Check(testkit.Rows("1 2")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + + tk.MustQuery("execute stmt3;").Check(testkit.Rows("1 2")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + tk.MustQuery("execute stmt3;").Check(testkit.Rows("1 2")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + + tk.MustQuery("execute stmt4;").Check(testkit.Rows("1 2")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + tk.MustQuery("execute stmt4;").Check(testkit.Rows("1 2")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + + tk.MustExec("alter table view_t drop column a") + tk.MustExec("alter table view_t add column a int after b") + tk.MustExec("update view_t set a=1;") + + tk.MustQuery("execute stmt1;").Check(testkit.Rows("1 2")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + tk.MustQuery("execute stmt1;").Check(testkit.Rows("1 2")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + + tk.MustQuery("execute stmt2;").Check(testkit.Rows("1 2")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + tk.MustQuery("execute stmt2;").Check(testkit.Rows("1 2")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + + tk.MustQuery("execute stmt3;").Check(testkit.Rows("1 2")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + tk.MustQuery("execute stmt3;").Check(testkit.Rows("1 2")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + + tk.MustQuery("execute stmt4;").Check(testkit.Rows("1 2")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + tk.MustQuery("execute stmt4;").Check(testkit.Rows("1 2")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + + tk.MustExec("drop table view_t;") + tk.MustExec("drop view view1,view2,view3,view4;") + + tk.MustExec("set @@tidb_enable_window_function = 1") + defer func() { + tk.MustExec("set @@tidb_enable_window_function = 0") + }() + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a int, b int)") + tk.MustExec("insert into t values (1,1),(1,2),(2,1),(2,2)") + tk.MustExec("create definer='root'@'localhost' view v as select a, first_value(a) over(rows between 1 preceding and 1 following), last_value(a) over(rows between 1 preceding and 1 following) from t") + tk.MustExec("prepare stmt from 'select * from v;';") + tk.MustQuery("execute stmt;").Check(testkit.Rows("1 1 1", "1 1 2", "2 1 2", "2 2 2")) + tk.MustQuery("execute stmt;").Check(testkit.Rows("1 1 1", "1 1 2", "2 1 2", "2 2 2")) + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) + + tk.MustExec("drop view v;") +} + +func (s *testPrepareSerialSuite) TestInvisibleIndex4PlanCache(c *C) { + tk := testkit.NewTestKitWithInit(c, s.store) + + orgEnable := core.PreparedPlanCacheEnabled() + defer func() { + core.SetPreparedPlanCache(orgEnable) + }() + core.SetPreparedPlanCache(true) + + var err error + tk.Se, err = session.CreateSession4TestWithOpt(s.store, &session.Opt{ + PreparedPlanCache: kvcache.NewSimpleLRUCache(100, 0.1, math.MaxUint64), + }) + c.Assert(err, IsNil) + + tk.MustExec("use test") + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + tk.MustExec("drop table if exists t;") + tk.MustExec("CREATE TABLE t(c1 INT, index idx_c(c1));") + + tk.MustExec("prepare stmt from 'select * from t use index(idx_c) where c1 > 1;';") + 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("ALTER TABLE t ALTER INDEX idx_c INVISIBLE;") + err = tk.ExecToErr("select * from t use index(idx_c) where c1 > 1;") + c.Assert(err.Error(), Equals, "[planner:1176]Key 'idx_c' doesn't exist in table 't'") + + err = tk.ExecToErr("execute stmt;") + c.Assert(err.Error(), Equals, "[planner:1176]Key 'idx_c' doesn't exist in table 't'") +} + +func (s *testPrepareSerialSuite) TestCTE4PlanCache(c *C) { + // CTE can not be cached, because part of it will be treated as a subquery. + tk := testkit.NewTestKitWithInit(c, s.store) + + orgEnable := core.PreparedPlanCacheEnabled() + defer func() { + core.SetPreparedPlanCache(orgEnable) + }() + core.SetPreparedPlanCache(true) + + var err error + tk.Se, err = session.CreateSession4TestWithOpt(s.store, &session.Opt{ + PreparedPlanCache: kvcache.NewSimpleLRUCache(100, 0.1, math.MaxUint64), + }) + c.Assert(err, IsNil) + + tk.MustExec("use test") + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + tk.MustExec("prepare stmt from 'with recursive cte1 as (" + + "select ? c1 " + + "union all " + + "select c1 + 1 c1 from cte1 where c1 < ?) " + + "select * from cte1;';") + tk.MustExec("set @a=5, @b=4, @c=2, @d=1;") + tk.MustQuery("execute stmt using @d, @a").Check(testkit.Rows("1", "2", "3", "4", "5")) + tk.MustQuery("execute stmt using @d, @b").Check(testkit.Rows("1", "2", "3", "4")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + tk.MustQuery("execute stmt using @c, @b").Check(testkit.Rows("2", "3", "4")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + + // Two seed parts. + tk.MustExec("prepare stmt from 'with recursive cte1 as (" + + "select 1 c1 " + + "union all " + + "select 2 c1 " + + "union all " + + "select c1 + 1 c1 from cte1 where c1 < ?) " + + "select * from cte1 order by c1;';") + tk.MustExec("set @a=10, @b=2;") + tk.MustQuery("execute stmt using @a").Check(testkit.Rows("1", "2", "2", "3", "3", "4", "4", "5", "5", "6", "6", "7", "7", "8", "8", "9", "9", "10", "10")) + tk.MustQuery("execute stmt using @b").Check(testkit.Rows("1", "2", "2")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + + // Two recursive parts. + tk.MustExec("prepare stmt from 'with recursive cte1 as (" + + "select 1 c1 " + + "union all " + + "select 2 c1 " + + "union all " + + "select c1 + 1 c1 from cte1 where c1 < ? " + + "union all " + + "select c1 + ? c1 from cte1 where c1 < ?) " + + "select * from cte1 order by c1;';") + tk.MustExec("set @a=1, @b=2, @c=3, @d=4, @e=5;") + tk.MustQuery("execute stmt using @c, @b, @e;").Check(testkit.Rows("1", "2", "2", "3", "3", "3", "4", "4", "5", "5", "5", "6", "6")) + tk.MustQuery("execute stmt using @b, @a, @d;").Check(testkit.Rows("1", "2", "2", "2", "3", "3", "3", "4", "4", "4")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + + tk.MustExec("drop table if exists t1;") + tk.MustExec("create table t1(a int);") + tk.MustExec("insert into t1 values(1);") + tk.MustExec("insert into t1 values(2);") + tk.MustExec("prepare stmt from 'SELECT * FROM t1 dt WHERE EXISTS(WITH RECURSIVE qn AS (SELECT a*? AS b UNION ALL SELECT b+? FROM qn WHERE b=?) SELECT * FROM qn WHERE b=a);';") + tk.MustExec("set @a=1, @b=2, @c=3, @d=4, @e=5, @f=0;") + + tk.MustQuery("execute stmt using @f, @a, @f").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt using @a, @b, @a").Check(testkit.Rows("1")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + + tk.MustExec("prepare stmt from 'with recursive c(p) as (select ?), cte(a, b) as (select 1, 1 union select a+?, 1 from cte, c where a < ?) select * from cte order by 1, 2;';") + tk.MustQuery("execute stmt using @a, @a, @e;").Check(testkit.Rows("1 1", "2 1", "3 1", "4 1", "5 1")) + tk.MustQuery("execute stmt using @b, @b, @c;").Check(testkit.Rows("1 1", "3 1")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) +} + +func (s *testPrepareSerialSuite) TestValidity4PlanCache(c *C) { + tk := testkit.NewTestKitWithInit(c, s.store) + + orgEnable := core.PreparedPlanCacheEnabled() + defer func() { + core.SetPreparedPlanCache(orgEnable) + }() + core.SetPreparedPlanCache(true) + + var err error + tk.Se, err = session.CreateSession4TestWithOpt(s.store, &session.Opt{ + PreparedPlanCache: kvcache.NewSimpleLRUCache(100, 0.1, math.MaxUint64), + }) + c.Assert(err, IsNil) + + tk.MustExec("use test") + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a int);") + + tk.MustExec("prepare stmt from 'select * from t;';") + 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("drop database if exists plan_cache;") + tk.MustExec("create database plan_cache;") + tk.MustExec("use plan_cache;") + tk.MustExec("create table t(a int);") + tk.MustExec("insert into t values(1);") + tk.MustQuery("execute stmt;").Check(testkit.Rows()) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + 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;';") + tk.MustQuery("execute stmt;").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt;").Check(testkit.Rows("1")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + + tk.MustExec("use test") + tk.MustQuery("execute stmt;").Check(testkit.Rows("1")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) +} + +func (s *testPrepareSerialSuite) TestListPartition4PlanCache(c *C) { + tk := testkit.NewTestKitWithInit(c, s.store) + + orgEnable := core.PreparedPlanCacheEnabled() + defer func() { + core.SetPreparedPlanCache(orgEnable) + }() + core.SetPreparedPlanCache(true) + + var err error + tk.Se, err = session.CreateSession4TestWithOpt(s.store, &session.Opt{ + PreparedPlanCache: kvcache.NewSimpleLRUCache(100, 0.1, math.MaxUint64), + }) + c.Assert(err, IsNil) + + tk.MustExec("use test") + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + tk.MustExec("set @@session.tidb_enable_list_partition=1;") + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a int, b int) PARTITION BY LIST (a) ( PARTITION p0 VALUES IN (1, 2, 3), PARTITION p1 VALUES IN (4, 5, 6));") + + tk.MustExec("set @@tidb_partition_prune_mode='static';") + tk.MustExec("prepare stmt from 'select * from t;';") + tk.MustQuery("execute stmt;").Check(testkit.Rows()) + tk.MustQuery("execute stmt;").Check(testkit.Rows()) + // The list partition plan can not be cached. + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) +} + +func (s *testSerialSuite) TestMoreSessions4PlanCache(c *C) { + tk := testkit.NewTestKitWithInit(c, s.store) + tk2 := testkit.NewTestKitWithInit(c, s.store) + + orgEnable := core.PreparedPlanCacheEnabled() + defer func() { + core.SetPreparedPlanCache(orgEnable) + }() + core.SetPreparedPlanCache(true) + + var err error + tk.Se, err = session.CreateSession4TestWithOpt(s.store, &session.Opt{ + PreparedPlanCache: kvcache.NewSimpleLRUCache(100, 0.1, math.MaxUint64), + }) + c.Assert(err, IsNil) + + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + tk.MustExec("use test;") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int);") + tk.MustExec("prepare stmt from 'select * from t;';") + + 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")) + + tk2.Se, err = session.CreateSession4TestWithOpt(s.store, &session.Opt{ + PreparedPlanCache: kvcache.NewSimpleLRUCache(100, 0.1, math.MaxUint64), + }) + c.Assert(err, IsNil) + + tk2.MustExec("use test;") + err = tk2.ExecToErr("execute stmt;") + c.Assert(err.Error(), Equals, "[planner:8111]Prepared statement not found") + tk2.MustExec("prepare stmt from 'select * from t;';") + tk2.MustQuery("execute stmt").Check(testkit.Rows()) + tk2.MustQuery("execute stmt").Check(testkit.Rows()) + tk2.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) + + tk.MustQuery("execute stmt").Check(testkit.Rows()) + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) +} + +func (s *testSuite) TestIssue28792(c *C) { + tk := testkit.NewTestKitWithInit(c, s.store) + tk.MustExec("use test") + tk.MustExec("CREATE TABLE t12(a INT, b INT)") + tk.MustExec("CREATE TABLE t97(a INT, b INT UNIQUE NOT NULL);") + r1 := tk.MustQuery("EXPLAIN SELECT t12.a, t12.b FROM t12 LEFT JOIN t97 on t12.b = t97.b;").Rows() + r2 := tk.MustQuery("EXPLAIN SELECT t12.a, t12.b FROM t12 LEFT JOIN t97 use index () on t12.b = t97.b;").Rows() + c.Assert(r1, DeepEquals, r2) +} +>>>>>>> 83e559db0... planner: allow refineArgs for plan cache in some situations (#28944) diff --git a/executor/prepared_test.go b/executor/prepared_test.go index 7e2536de534d3..4aa7cae8d5dfd 100644 --- a/executor/prepared_test.go +++ b/executor/prepared_test.go @@ -429,7 +429,7 @@ func (s *testSerialSuite) TestIssue28087And28162(c *C) { tk.MustQuery(`execute stmt using @a,@b,@c`).Check(testkit.Rows("\x01")) tk.MustExec(`set @a=0x00, @b=0x00, @c=0x01`) tk.MustQuery(`execute stmt using @a,@b,@c`).Check(testkit.Rows("\x00", "\x01")) - tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1")) + tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0")) // issue 28162 tk.MustExec(`drop table if exists IDT_MC21780`) diff --git a/executor/testdata/prepare_suite_out.json b/executor/testdata/prepare_suite_out.json index be009dc7401bf..e48bd040e2d6e 100644 --- a/executor/testdata/prepare_suite_out.json +++ b/executor/testdata/prepare_suite_out.json @@ -134,10 +134,16 @@ } ], "Plan": [ +<<<<<<< HEAD "Projection_4 8000.00 root test.t1.a", "└─IndexReader_10 8000.00 root index:Selection_9", " └─Selection_9 8000.00 cop[tikv] eq(cast(test.t1.b, double BINARY), 0)", " └─IndexFullScan_8 10000.00 cop[tikv] table:t1, index:b(b, a) keep order:false, stats:pseudo" +======= + "Projection_4 10.00 root test.t1.a", + "└─IndexReader_6 10.00 root index:IndexRangeScan_5", + " └─IndexRangeScan_5 10.00 cop[tikv] table:t1, index:b(b, a) range:[0,0], keep order:false, stats:pseudo" +>>>>>>> 83e559db0... planner: allow refineArgs for plan cache in some situations (#28944) ], "LastPlanUseCache": "0", "Result": null @@ -202,6 +208,7 @@ } ], "Plan": [ +<<<<<<< HEAD "HashJoin_38 63744383.74 root inner join, equal:[eq(test.t1.b, test.t2.b) eq(test.t1.a, test.t2.a)]", "├─TableReader_59(Build) 7984.01 root data:Selection_58", "│ └─Selection_58 7984.01 cop[tikv] eq(cast(test.t2.b, double BINARY), 0), not(isnull(test.t2.a)), not(isnull(test.t2.b))", @@ -209,6 +216,15 @@ "└─TableReader_52(Probe) 7984.01 root data:Selection_51", " └─Selection_51 7984.01 cop[tikv] eq(cast(test.t1.b, double BINARY), 0), not(isnull(test.t1.a)), not(isnull(test.t1.b))", " └─TableFullScan_50 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo" +======= + "HashJoin_36 124.88 root inner join, equal:[eq(test.t1.a, test.t2.a)]", + "├─IndexLookUp_57(Build) 99.90 root ", + "│ ├─IndexRangeScan_55(Build) 99.90 cop[tikv] table:t2, index:b(b, a) range:[0 -inf,0 +inf], keep order:false, stats:pseudo", + "│ └─TableRowIDScan_56(Probe) 99.90 cop[tikv] table:t2 keep order:false, stats:pseudo", + "└─IndexLookUp_51(Probe) 99.90 root ", + " ├─IndexRangeScan_49(Build) 99.90 cop[tikv] table:t1, index:b(b, a) range:[0 -inf,0 +inf], keep order:false, stats:pseudo", + " └─TableRowIDScan_50(Probe) 99.90 cop[tikv] table:t1 keep order:false, stats:pseudo" +>>>>>>> 83e559db0... planner: allow refineArgs for plan cache in some situations (#28944) ], "LastPlanUseCache": "0", "Result": null diff --git a/expression/builtin_compare.go b/expression/builtin_compare.go index d28113e51e88c..2c6d8c13f38cb 100644 --- a/expression/builtin_compare.go +++ b/expression/builtin_compare.go @@ -1360,17 +1360,34 @@ func RefineComparedConstant(ctx sessionctx.Context, targetFieldType types.FieldT // refineArgs will rewrite the arguments if the compare expression is `int column non-int constant` or // `non-int constant int column`. E.g., `a < 1.1` will be rewritten to `a < 2`. It also handles comparing year type // with int constant if the int constant falls into a sensible year representation. +// This refine operation depends on the values of these args, but these values can change when using plan-cache. +// So we have to skip this operation or mark the plan as over-optimized when using plan-cache. func (c *compareFunctionClass) refineArgs(ctx sessionctx.Context, args []Expression) []Expression { +<<<<<<< HEAD if ContainMutableConst(ctx, args) { return args } +======= +>>>>>>> 83e559db0... planner: allow refineArgs for plan cache in some situations (#28944) arg0Type, arg1Type := args[0].GetType(), args[1].GetType() arg0IsInt := arg0Type.EvalType() == types.ETInt arg1IsInt := arg1Type.EvalType() == types.ETInt + arg0IsString := arg0Type.EvalType() == types.ETString + arg1IsString := arg1Type.EvalType() == types.ETString arg0, arg0IsCon := args[0].(*Constant) arg1, arg1IsCon := args[1].(*Constant) isExceptional, finalArg0, finalArg1 := false, args[0], args[1] isPositiveInfinite, isNegativeInfinite := false, false + if MaybeOverOptimized4PlanCache(ctx, args) { + // To keep the result be compatible with MySQL, refine `int non-constant str constant` + // here and skip this refine operation in all other cases for safety. + if (arg0IsInt && !arg0IsCon && arg1IsString && arg1IsCon) || (arg1IsInt && !arg1IsCon && arg0IsString && arg0IsCon) { + ctx.GetSessionVars().StmtCtx.MaybeOverOptimized4PlanCache = true + RemoveMutableConst(ctx, args) + } else { + return args + } + } // int non-constant [cmp] non-int constant if arg0IsInt && !arg0IsCon && !arg1IsInt && arg1IsCon { arg1, isExceptional = RefineComparedConstant(ctx, *arg0Type, arg1, c.op) diff --git a/expression/integration_test.go b/expression/integration_test.go index e2f620da24811..1562783287014 100644 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -6267,6 +6267,10 @@ func (s *testIntegrationSerialSuite) TestCacheRefineArgs(c *C) { tk.MustExec("set @p0='0'") tk.MustQuery("execute stmt using @p0").Check(testkit.Rows("1")) + tk.MustExec("prepare stmt from 'SELECT UCASE(?) < col_int from t;';") + tk.MustExec("set @a1 = 'xayh7vrWVNqZtzlJmdJQUwAHnkI8Ec';") + tk.MustQuery("execute stmt using @a1;").Check(testkit.Rows("")) + tk.MustExec("delete from t") tk.MustExec("insert into t values(1)") tk.MustExec("prepare stmt from 'SELECT col_int < ? FROM t'") diff --git a/expression/util.go b/expression/util.go index 56d526495638f..7656ab245e73a 100644 --- a/expression/util.go +++ b/expression/util.go @@ -879,12 +879,42 @@ func ContainCorrelatedColumn(exprs []Expression) bool { return false } +<<<<<<< HEAD // ContainMutableConst checks if the expressions contain a lazy constant. func ContainMutableConst(ctx sessionctx.Context, exprs []Expression) bool { // Treat all constants immutable if plan cache is not enabled for this query. if !ctx.GetSessionVars().StmtCtx.UseCache { return false } +======= +// MaybeOverOptimized4PlanCache used to check whether an optimization can work +// for the statement when we enable the plan cache. +// In some situations, some optimizations maybe over-optimize and cache an +// overOptimized plan. The cached plan may not get the correct result when we +// reuse the plan for other statements. +// For example, `pk>=$a and pk<=$b` can be optimized to a PointGet when +// `$a==$b`, but it will cause wrong results when `$a!=$b`. +// So we need to do the check here. The check includes the following aspects: +// 1. Whether the plan cache switch is enable. +// 2. Whether the statement can be cached. +// 3. Whether the expressions contain a lazy constant. +// TODO: Do more careful check here. +func MaybeOverOptimized4PlanCache(ctx sessionctx.Context, exprs []Expression) bool { + // If we do not enable plan cache, all the optimization can work correctly. + if !ctx.GetSessionVars().StmtCtx.UseCache { + return false + } + if ctx.GetSessionVars().StmtCtx.MaybeOverOptimized4PlanCache { + // If the current statement can not be cached. We should remove the mutable constant. + RemoveMutableConst(ctx, exprs) + return false + } + return containMutableConst(ctx, exprs) +} + +// containMutableConst checks if the expressions contain a lazy constant. +func containMutableConst(ctx sessionctx.Context, exprs []Expression) bool { +>>>>>>> 83e559db0... planner: allow refineArgs for plan cache in some situations (#28944) for _, expr := range exprs { switch v := expr.(type) { case *Constant: @@ -900,6 +930,19 @@ func ContainMutableConst(ctx sessionctx.Context, exprs []Expression) bool { return false } +// RemoveMutableConst used to remove the `ParamMarker` and `DeferredExpr` in the `Constant` expr. +func RemoveMutableConst(ctx sessionctx.Context, exprs []Expression) { + for _, expr := range exprs { + switch v := expr.(type) { + case *Constant: + v.ParamMarker = nil + v.DeferredExpr = nil + case *ScalarFunction: + RemoveMutableConst(ctx, v.GetArgs()) + } + } +} + const ( _ = iota kib = 1 << (10 * iota) diff --git a/planner/core/expression_rewriter.go b/planner/core/expression_rewriter.go index b7b7ad442794e..717d1107f7958 100644 --- a/planner/core/expression_rewriter.go +++ b/planner/core/expression_rewriter.go @@ -1418,11 +1418,25 @@ func (er *expressionRewriter) inToExpression(lLen int, not bool, tp *types.Field er.ctxStackAppend(expression.NewNull(), types.EmptyName) return } +<<<<<<< HEAD containMut := expression.ContainMutableConst(er.sctx, args) if !containMut && leftEt == types.ETInt { +======= + if leftEt == types.ETInt { +>>>>>>> 83e559db0... planner: allow refineArgs for plan cache in some situations (#28944) for i := 1; i < len(args); i++ { if c, ok := args[i].(*expression.Constant); ok { var isExceptional bool + if expression.MaybeOverOptimized4PlanCache(er.sctx, []expression.Expression{c}) { + if c.GetType().EvalType() == types.ETString { + // To keep the result be compatible with MySQL, refine `int non-constant str constant` + // here and skip this refine operation in all other cases for safety. + er.sctx.GetSessionVars().StmtCtx.MaybeOverOptimized4PlanCache = true + expression.RemoveMutableConst(er.sctx, []expression.Expression{c}) + } else { + continue + } + } args[i], isExceptional = expression.RefineComparedConstant(er.sctx, *leftFt, c, opcode.EQ) if isExceptional { args[i] = c diff --git a/planner/core/prepare_test.go b/planner/core/prepare_test.go index 87d9741dc3d7c..89da3a55aa427 100644 --- a/planner/core/prepare_test.go +++ b/planner/core/prepare_test.go @@ -925,6 +925,141 @@ func (s *testPlanSerialSuite) TestIssue28254(c *C) { tk.MustQuery("execute stmt using @a").Check(testkit.Rows("1")) } +<<<<<<< HEAD +======= +func (s *testPlanSerialSuite) TestIssue28867(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), + }) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2") + tk.MustExec(`CREATE TABLE t1 (c_int int, c_str varchar(40), PRIMARY KEY (c_int, c_str))`) + tk.MustExec(`CREATE TABLE t2 (c_str varchar(40), PRIMARY KEY (c_str))`) + tk.MustExec(`insert into t1 values (1, '1')`) + tk.MustExec(`insert into t2 values ('1')`) + + tk.MustExec(`prepare stmt from 'select /*+ INL_JOIN(t1,t2) */ * from t1 join t2 on t1.c_str <= t2.c_str where t1.c_int in (?,?)'`) + tk.MustExec(`set @a=10, @b=20`) + tk.MustQuery(`execute stmt using @a, @b`).Check(testkit.Rows()) + tk.MustExec(`set @a=1, @b=2`) + tk.MustQuery(`execute stmt using @a, @b`).Check(testkit.Rows("1 1 1")) + + // test case for IndexJoin + PlanCache + tk.MustExec(`drop table t1, t2`) + tk.MustExec(`create table t1 (a int, b int, c int, index idxab(a, b, c))`) + tk.MustExec(`create table t2 (a int, b int)`) + + tk.MustExec(`prepare stmt from 'select /*+ INL_JOIN(t1,t2) */ * from t1, t2 where t1.a=t2.a and t1.b=?'`) + tk.MustExec(`set @a=1`) + tk.MustExec(`execute stmt using @a`) + tk.MustExec(`execute stmt using @a`) + // the index range [a, b] depends on parameters, so it cannot use plan-cache + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) + + tk.MustExec(`prepare stmt from 'select /*+ INL_JOIN(t1,t2) */ * from t1, t2 where t1.a=t2.a and t1.c=?'`) + tk.MustExec(`set @a=1`) + tk.MustExec(`execute stmt using @a`) + tk.MustExec(`execute stmt using @a`) + // the index range [a] doesn't depend on parameters, so it can use plan-cache + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) +} + +func (s *testPlanSerialSuite) TestIssue28828(c *C) { + store, dom, err := newStoreWithBootstrap() + c.Assert(err, IsNil) + tk := testkit.NewTestKit(c, store) + defer func() { + dom.Close() + store.Close() + }() + orgEnable := core.PreparedPlanCacheEnabled() + defer func() { + core.SetPreparedPlanCache(orgEnable) + }() + core.SetPreparedPlanCache(true) + tk.MustExec("use test") + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + tk.MustExec("CREATE TABLE t (" + + "id bigint(20) NOT NULL," + + "audit_id bigint(20) NOT NULL," + + "PRIMARY KEY (id) /*T![clustered_index] CLUSTERED */," + + "KEY index_audit_id (audit_id)" + + ");") + tk.MustExec("insert into t values(1,9941971237863475), (2,9941971237863476), (3, 0);") + tk.MustExec("prepare stmt from 'select * from t where audit_id=?';") + tk.MustExec("set @a='9941971237863475', @b=9941971237863475, @c='xayh7vrWVNqZtzlJmdJQUwAHnkI8Ec', @d='0.0', @e='0.1', @f = '9941971237863476';") + + tk.MustQuery("execute stmt using @a;").Check(testkit.Rows("1 9941971237863475")) + tk.MustQuery("execute stmt using @b;").Check(testkit.Rows("1 9941971237863475")) + // When the type of parameters have been changed, the plan cache can not be used. + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + tk.MustQuery("execute stmt using @c;").Check(testkit.Rows("3 0")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + tk.MustQuery("execute stmt using @d;").Check(testkit.Rows("3 0")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + tk.MustQuery("execute stmt using @e;").Check(testkit.Rows()) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + tk.MustQuery("execute stmt using @d;").Check(testkit.Rows("3 0")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + tk.MustQuery("execute stmt using @f;").Check(testkit.Rows("2 9941971237863476")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + tk.MustExec("prepare stmt from 'select count(*) from t where audit_id in (?, ?, ?, ?, ?)';") + tk.MustQuery("execute stmt using @a, @b, @c, @d, @e;").Check(testkit.Rows("2")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + tk.MustQuery("execute stmt using @f, @b, @c, @d, @e;").Check(testkit.Rows("3")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) +} + +func (s *testPlanSerialSuite) TestIssue28920(c *C) { + store, dom, err := newStoreWithBootstrap() + c.Assert(err, IsNil) + tk := testkit.NewTestKit(c, store) + orgEnable := core.PreparedPlanCacheEnabled() + defer func() { + dom.Close() + c.Assert(store.Close(), 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 UK_GCOL_VIRTUAL_18928`) + tk.MustExec(` + CREATE TABLE UK_GCOL_VIRTUAL_18928 ( + COL102 bigint(20) DEFAULT NULL, + COL103 bigint(20) DEFAULT NULL, + COL1 bigint(20) GENERATED ALWAYS AS (COL102 & 10) VIRTUAL, + COL2 varchar(20) DEFAULT NULL, + COL4 datetime DEFAULT NULL, + COL3 bigint(20) DEFAULT NULL, + COL5 float DEFAULT NULL, + UNIQUE KEY UK_COL1 (COL1))`) + tk.MustExec(`insert into UK_GCOL_VIRTUAL_18928(col102,col2) values("-5175976006730879891", "屘厒镇览錻碛斵大擔觏譨頙硺箄魨搝珄鋧扭趖")`) + tk.MustExec(`prepare stmt from 'SELECT * FROM UK_GCOL_VIRTUAL_18928 WHERE col1 < ? AND col2 != ?'`) + tk.MustExec(`set @a=10, @b="aa"`) + tk.MustQuery(`execute stmt using @a, @b`).Check(testkit.Rows("-5175976006730879891 8 屘厒镇览錻碛斵大擔觏譨頙硺箄魨搝珄鋧扭趖 ")) +} + +>>>>>>> 83e559db0... planner: allow refineArgs for plan cache in some situations (#28944) func (s *testPlanSerialSuite) TestIssue18066(c *C) { defer testleak.AfterTest(c)() store, dom, err := newStoreWithBootstrap()