From 4684c90e3906ab16b31e64e07c40e00e0353c8ea Mon Sep 17 00:00:00 2001 From: guo-shaoge Date: Thu, 6 Jan 2022 15:34:38 +0800 Subject: [PATCH] cherry pick #31256 to release-5.2 Signed-off-by: ti-srebot --- cmd/explaintest/r/cte.result | 115 ++++ cmd/explaintest/t/cte.test | 30 + executor/builder.go | 14 +- executor/cte.go | 17 +- executor/explainfor_test.go | 950 +++++++++++++++++++++++++++ planner/core/logical_plan_builder.go | 20 + planner/core/logical_plans.go | 9 +- 7 files changed, 1138 insertions(+), 17 deletions(-) diff --git a/cmd/explaintest/r/cte.result b/cmd/explaintest/r/cte.result index 9d27b786bf446..97f3a9d265ba2 100644 --- a/cmd/explaintest/r/cte.result +++ b/cmd/explaintest/r/cte.result @@ -607,3 +607,118 @@ c1 c1 c1 1 1 1 2 2 2 3 3 3 +// Test CTE as inner side of Apply +drop table if exists t1, t2; +create table t1(c1 int, c2 int); +insert into t1 values(2, 1); +insert into t1 values(2, 2); +create table t2(c1 int, c2 int); +insert into t2 values(1, 1); +insert into t2 values(3, 2); +explain select * from t1 where c1 > all(with cte1 as (select c1 from t2 where t2.c2 = t1.c2) select c1 from cte1); +id estRows task access object operator info +Projection_17 10000.00 root test.t1.c1, test.t1.c2 +└─Apply_19 10000.00 root CARTESIAN inner join, other cond:or(and(gt(test.t1.c1, Column#8), if(ne(Column#9, 0), NULL, 1)), or(eq(Column#10, 0), if(isnull(test.t1.c1), NULL, 0))) + ├─TableReader_21(Build) 10000.00 root data:TableFullScan_20 + │ └─TableFullScan_20 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo + └─HashAgg_22(Probe) 1.00 root funcs:max(Column#13)->Column#8, funcs:sum(Column#14)->Column#9, funcs:count(1)->Column#10 + └─Projection_26 10.00 root test.t2.c1, cast(isnull(test.t2.c1), decimal(20,0) BINARY)->Column#14 + └─CTEFullScan_24 10.00 root CTE:cte1 data:CTE_0 +CTE_0 10.00 root Non-Recursive CTE +└─Projection_12(Seed Part) 10.00 root test.t2.c1 + └─TableReader_15 10.00 root data:Selection_14 + └─Selection_14 10.00 cop[tikv] eq(test.t2.c2, test.t1.c2) + └─TableFullScan_13 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo +select * from t1 where c1 > all(with cte1 as (select c1 from t2 where t2.c2 = t1.c2) select c1 from cte1); +c1 c2 +2 1 +// Test semi apply. +insert into t1 values(2, 3); +explain select * from t1 where exists(with cte1 as (select c1 from t2 where t2.c2 = t1.c2) select c1 from cte1); +id estRows task access object operator info +Apply_16 10000.00 root CARTESIAN semi join +├─TableReader_18(Build) 10000.00 root data:TableFullScan_17 +│ └─TableFullScan_17 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo +└─CTEFullScan_19(Probe) 10.00 root CTE:cte1 data:CTE_0 +CTE_0 10.00 root Non-Recursive CTE +└─Projection_10(Seed Part) 10.00 root test.t2.c1 + └─TableReader_13 10.00 root data:Selection_12 + └─Selection_12 10.00 cop[tikv] eq(test.t2.c2, test.t1.c2) + └─TableFullScan_11 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo +select * from t1 where exists(with cte1 as (select c1 from t2 where t2.c2 = t1.c2) select c1 from cte1); +c1 c2 +2 1 +2 2 +// Same as above, but test recursive cte. +explain select * from t1 where c1 > all(with recursive cte1 as (select c1 from t2 where t2.c2 = t1.c2 union all select c1+1 as c1 from cte1 limit 1) select c1 from cte1); +id estRows task access object operator info +Projection_26 10000.00 root test.t1.c1, test.t1.c2 +└─Apply_28 10000.00 root CARTESIAN inner join, other cond:or(and(gt(test.t1.c1, Column#14), if(ne(Column#15, 0), NULL, 1)), or(eq(Column#16, 0), if(isnull(test.t1.c1), NULL, 0))) + ├─TableReader_30(Build) 10000.00 root data:TableFullScan_29 + │ └─TableFullScan_29 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo + └─HashAgg_31(Probe) 1.00 root funcs:max(Column#19)->Column#14, funcs:sum(Column#20)->Column#15, funcs:count(1)->Column#16 + └─Projection_35 20.00 root test.t2.c1, cast(isnull(test.t2.c1), decimal(20,0) BINARY)->Column#20 + └─CTEFullScan_33 20.00 root CTE:cte1 data:CTE_0 +CTE_0 20.00 root Recursive CTE, limit(offset:0, count:1) +├─Projection_19(Seed Part) 10.00 root test.t2.c1 +│ └─TableReader_22 10.00 root data:Selection_21 +│ └─Selection_21 10.00 cop[tikv] eq(test.t2.c2, test.t1.c2) +│ └─TableFullScan_20 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo +└─Projection_23(Recursive Part) 10.00 root cast(plus(test.t2.c1, 1), int(11))->test.t2.c1 + └─CTETable_24 10.00 root Scan on CTE_0 +select * from t1 where c1 > all(with recursive cte1 as (select c1 from t2 where t2.c2 = t1.c2 union all select c1+1 as c1 from cte1 limit 1) select c1 from cte1); +c1 c2 +2 1 +2 3 +explain select * from t1 where exists(with recursive cte1 as (select c1 from t2 where t2.c2 = t1.c2 union all select c1+1 as c1 from cte1 limit 10) select c1 from cte1); +id estRows task access object operator info +Apply_25 10000.00 root CARTESIAN semi join +├─TableReader_27(Build) 10000.00 root data:TableFullScan_26 +│ └─TableFullScan_26 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo +└─CTEFullScan_28(Probe) 20.00 root CTE:cte1 data:CTE_0 +CTE_0 20.00 root Recursive CTE, limit(offset:0, count:10) +├─Projection_17(Seed Part) 10.00 root test.t2.c1 +│ └─TableReader_20 10.00 root data:Selection_19 +│ └─Selection_19 10.00 cop[tikv] eq(test.t2.c2, test.t1.c2) +│ └─TableFullScan_18 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo +└─Projection_21(Recursive Part) 10.00 root cast(plus(test.t2.c1, 1), int(11))->test.t2.c1 + └─CTETable_22 10.00 root Scan on CTE_0 +select * from t1 where exists(with recursive cte1 as (select c1 from t2 where t2.c2 = t1.c2 union all select c1+1 as c1 from cte1 limit 10) select c1 from cte1); +c1 c2 +2 1 +2 2 +// Test correlated col is in recursive part. +explain select * from t1 where c1 > all(with recursive cte1 as (select c1, c2 from t2 union all select c1+1 as c1, c2+1 as c2 from cte1 where cte1.c2=t1.c2) select c1 from cte1); +id estRows task access object operator info +Projection_24 10000.00 root test.t1.c1, test.t1.c2 +└─Apply_26 10000.00 root CARTESIAN inner join, other cond:or(and(gt(test.t1.c1, Column#18), if(ne(Column#19, 0), NULL, 1)), or(eq(Column#20, 0), if(isnull(test.t1.c1), NULL, 0))) + ├─TableReader_28(Build) 10000.00 root data:TableFullScan_27 + │ └─TableFullScan_27 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo + └─HashAgg_29(Probe) 1.00 root funcs:max(Column#23)->Column#18, funcs:sum(Column#24)->Column#19, funcs:count(1)->Column#20 + └─Projection_33 18000.00 root test.t2.c1, cast(isnull(test.t2.c1), decimal(20,0) BINARY)->Column#24 + └─CTEFullScan_31 18000.00 root CTE:cte1 data:CTE_0 +CTE_0 18000.00 root Recursive CTE +├─TableReader_19(Seed Part) 10000.00 root data:TableFullScan_18 +│ └─TableFullScan_18 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo +└─Projection_20(Recursive Part) 8000.00 root cast(plus(test.t2.c1, 1), int(11))->test.t2.c1, cast(plus(test.t2.c2, 1), int(11))->test.t2.c2 + └─Selection_21 8000.00 root eq(test.t2.c2, test.t1.c2) + └─CTETable_22 10000.00 root Scan on CTE_0 +select * from t1 where c1 > all(with recursive cte1 as (select c1, c2 from t2 union all select c1+1 as c1, c2+1 as c2 from cte1 where cte1.c2=t1.c2) select c1 from cte1); +c1 c2 +explain select * from t1 where exists(with recursive cte1 as (select c1, c2 from t2 union all select c1+1 as c1, c2+1 as c2 from cte1 where cte1.c2=t1.c2) select c1 from cte1); +id estRows task access object operator info +Apply_23 10000.00 root CARTESIAN semi join +├─TableReader_25(Build) 10000.00 root data:TableFullScan_24 +│ └─TableFullScan_24 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo +└─CTEFullScan_26(Probe) 18000.00 root CTE:cte1 data:CTE_0 +CTE_0 18000.00 root Recursive CTE +├─TableReader_17(Seed Part) 10000.00 root data:TableFullScan_16 +│ └─TableFullScan_16 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo +└─Projection_18(Recursive Part) 8000.00 root cast(plus(test.t2.c1, 1), int(11))->test.t2.c1, cast(plus(test.t2.c2, 1), int(11))->test.t2.c2 + └─Selection_19 8000.00 root eq(test.t2.c2, test.t1.c2) + └─CTETable_20 10000.00 root Scan on CTE_0 +select * from t1 where exists(with recursive cte1 as (select c1, c2 from t2 union all select c1+1 as c1, c2+1 as c2 from cte1 where cte1.c2=t1.c2) select c1 from cte1); +c1 c2 +2 1 +2 2 +2 3 diff --git a/cmd/explaintest/t/cte.test b/cmd/explaintest/t/cte.test index c42d95cc6d8fb..bb01759dbd860 100644 --- a/cmd/explaintest/t/cte.test +++ b/cmd/explaintest/t/cte.test @@ -226,3 +226,33 @@ create table tpk1(c1 int primary key); insert into tpk1 values(1), (2), (3); explain with cte1 as (select c1 from tpk) select /*+ merge_join(dt1, dt2) */ * from tpk1 dt1 inner join cte1 dt2 inner join cte1 dt3 on dt1.c1 = dt2.c1 and dt2.c1 = dt3.c1; with cte1 as (select c1 from tpk) select /*+ merge_join(dt1, dt2) */ * from tpk1 dt1 inner join cte1 dt2 inner join cte1 dt3 on dt1.c1 = dt2.c1 and dt2.c1 = dt3.c1; +#case 34 +--echo // Test CTE as inner side of Apply +drop table if exists t1, t2; +create table t1(c1 int, c2 int); +insert into t1 values(2, 1); +insert into t1 values(2, 2); +create table t2(c1 int, c2 int); +insert into t2 values(1, 1); +insert into t2 values(3, 2); +explain select * from t1 where c1 > all(with cte1 as (select c1 from t2 where t2.c2 = t1.c2) select c1 from cte1); +select * from t1 where c1 > all(with cte1 as (select c1 from t2 where t2.c2 = t1.c2) select c1 from cte1); + +--echo // Test semi apply. +insert into t1 values(2, 3); +explain select * from t1 where exists(with cte1 as (select c1 from t2 where t2.c2 = t1.c2) select c1 from cte1); +select * from t1 where exists(with cte1 as (select c1 from t2 where t2.c2 = t1.c2) select c1 from cte1); + +--echo // Same as above, but test recursive cte. +explain select * from t1 where c1 > all(with recursive cte1 as (select c1 from t2 where t2.c2 = t1.c2 union all select c1+1 as c1 from cte1 limit 1) select c1 from cte1); +select * from t1 where c1 > all(with recursive cte1 as (select c1 from t2 where t2.c2 = t1.c2 union all select c1+1 as c1 from cte1 limit 1) select c1 from cte1); + +explain select * from t1 where exists(with recursive cte1 as (select c1 from t2 where t2.c2 = t1.c2 union all select c1+1 as c1 from cte1 limit 10) select c1 from cte1); +select * from t1 where exists(with recursive cte1 as (select c1 from t2 where t2.c2 = t1.c2 union all select c1+1 as c1 from cte1 limit 10) select c1 from cte1); + +--echo // Test correlated col is in recursive part. +explain select * from t1 where c1 > all(with recursive cte1 as (select c1, c2 from t2 union all select c1+1 as c1, c2+1 as c2 from cte1 where cte1.c2=t1.c2) select c1 from cte1); +select * from t1 where c1 > all(with recursive cte1 as (select c1, c2 from t2 union all select c1+1 as c1, c2+1 as c2 from cte1 where cte1.c2=t1.c2) select c1 from cte1); + +explain select * from t1 where exists(with recursive cte1 as (select c1, c2 from t2 union all select c1+1 as c1, c2+1 as c2 from cte1 where cte1.c2=t1.c2) select c1 from cte1); +select * from t1 where exists(with recursive cte1 as (select c1, c2 from t2 union all select c1+1 as c1, c2+1 as c2 from cte1 where cte1.c2=t1.c2) select c1 from cte1); diff --git a/executor/builder.go b/executor/builder.go index fd2b16321f215..65096c89c78ed 100644 --- a/executor/builder.go +++ b/executor/builder.go @@ -4432,19 +4432,9 @@ func (b *executorBuilder) buildCTE(v *plannercore.PhysicalCTE) Executor { // 2. Build tables to store intermediate results. chkSize := b.ctx.GetSessionVars().MaxChunkSize tps := seedExec.base().retFieldTypes + // iterOutTbl will be constructed in CTEExec.Open(). var resTbl cteutil.Storage var iterInTbl cteutil.Storage - var iterOutTbl cteutil.Storage - - if v.RecurPlan != nil { - // For non-recursive CTE, the result will be put into resTbl directly. - // So no need to build iterOutTbl. - iterOutTbl := cteutil.NewStorageRowContainer(tps, chkSize) - if err := iterOutTbl.OpenAndRef(); err != nil { - b.err = err - return nil - } - } storageMap, ok := b.ctx.GetSessionVars().StmtCtx.CTEStorageMap.(map[int]*CTEStorages) if !ok { @@ -4493,13 +4483,13 @@ func (b *executorBuilder) buildCTE(v *plannercore.PhysicalCTE) Executor { recursiveExec: recursiveExec, resTbl: resTbl, iterInTbl: iterInTbl, - iterOutTbl: iterOutTbl, chkIdx: 0, isDistinct: v.CTE.IsDistinct, sel: sel, hasLimit: v.CTE.HasLimit, limitBeg: v.CTE.LimitBeg, limitEnd: v.CTE.LimitEnd, + isInApply: v.CTE.IsInApply, } } diff --git a/executor/cte.go b/executor/cte.go index 4fe3414b97154..ee6b55a6e5991 100644 --- a/executor/cte.go +++ b/executor/cte.go @@ -88,6 +88,11 @@ type CTEExec struct { memTracker *memory.Tracker diskTracker *disk.Tracker + + // isInApply indicates whether CTE is in inner side of Apply + // and should resTbl/iterInTbl be reset for each outer row of Apply. + // Because we reset them when SQL is finished instead of when CTEExec.Close() is called. + isInApply bool } // Open implements the Executor interface. @@ -113,6 +118,9 @@ func (e *CTEExec) Open(ctx context.Context) (err error) { if err = e.recursiveExec.Open(ctx); err != nil { return err } + // For non-recursive CTE, the result will be put into resTbl directly. + // So no need to build iterOutTbl. + // Construct iterOutTbl in Open() instead of buildCTE(), because its destruct is in Close(). recursiveTypes := e.recursiveExec.base().retFieldTypes e.iterOutTbl = cteutil.NewStorageRowContainer(recursiveTypes, e.maxChunkSize) if err = e.iterOutTbl.OpenAndRef(); err != nil { @@ -207,6 +215,11 @@ func (e *CTEExec) Close() (err error) { return err } } + if e.isInApply { + if err = e.reopenTbls(); err != nil { + return err + } + } return e.baseExecutor.Close() } @@ -395,7 +408,9 @@ func (e *CTEExec) reset() { } func (e *CTEExec) reopenTbls() (err error) { - e.hashTbl = newConcurrentMapHashTable() + if e.isDistinct { + e.hashTbl = newConcurrentMapHashTable() + } if err := e.resTbl.Reopen(); err != nil { return err } diff --git a/executor/explainfor_test.go b/executor/explainfor_test.go index 89b9d934a4858..2e8a7a6bee243 100644 --- a/executor/explainfor_test.go +++ b/executor/explainfor_test.go @@ -525,3 +525,953 @@ 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("0")) + 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, 4) + c.Assert(res.Rows()[0][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[3][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("0")) + 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, ".*IndexRangeScan.*") + + 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("0")) + 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, ".*IndexRangeScan.*") + 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()[1][0], Matches, ".*IndexMerge.*") + + tk.MustQuery("execute stmt using @b;").Check(testkit.Rows("3 ddcdsaf 3")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + 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()[1][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")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + tk.MustExec("prepare stmt from 'select /*+ use_index_merge(t1) */ * from t1 where c=? or (b=? and (a=? or a=?));';") + tk.MustQuery("execute stmt using @a, @a, @a, @a").Check(testkit.Rows("10 10 10")) + tk.MustQuery("execute stmt using @b, @b, @b, @b").Check(testkit.Rows("11 11 11")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + tk.MustExec("prepare stmt from 'select /*+ use_index_merge(t1) */ * from t1 where c=? or (b=? and (a=? and c=?));';") + tk.MustQuery("execute stmt using @a, @a, @a, @a").Check(testkit.Rows("10 10 10")) + tk.MustQuery("execute stmt using @b, @b, @b, @b").Check(testkit.Rows("11 11 11")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + tk.MustExec("prepare stmt from 'select /*+ use_index_merge(t1) */ * from t1 where c=? or (b=? and (a >= ? and a <= ?));';") + tk.MustQuery("execute stmt using @a, @a, @b, @a").Check(testkit.Rows("10 10 10")) + tk.MustQuery("execute stmt using @b, @b, @b, @b").Check(testkit.Rows("11 11 11")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + + tk.MustExec("prepare stmt from 'select /*+ use_index_merge(t1) */ * from t1 where c=10 or (a >=? and a <= ?);';") + tk.MustExec("set @a=9, @b=10, @c=11;") + tk.MustQuery("execute stmt using @a, @a;").Check(testkit.Rows("10 10 10")) + tk.MustQuery("execute stmt using @a, @c;").Check(testkit.Rows("10 10 10", "11 11 11")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt using @c, @a;").Check(testkit.Rows("10 10 10")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + + tk.MustExec("prepare stmt from 'select /*+ use_index_merge(t1) */ * from t1 where c=10 or (a >=? and a <= ?);';") + tk.MustExec("set @a=9, @b=10, @c=11;") + tk.MustQuery("execute stmt using @a, @c;").Check(testkit.Rows("10 10 10", "11 11 11")) + tk.MustQuery("execute stmt using @a, @a;").Check(testkit.Rows("10 10 10")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt using @c, @a;").Check(testkit.Rows("10 10 10")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + + tk.MustExec("prepare stmt from 'select /*+ use_index_merge(t1) */ * from t1 where c=10 or (a >=? and a <= ?);';") + tk.MustExec("set @a=9, @b=10, @c=11;") + tk.MustQuery("execute stmt using @c, @a;").Check(testkit.Rows("10 10 10")) + tk.MustQuery("execute stmt using @a, @c;").Check(testkit.Rows("10 10 10", "11 11 11")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt using @a, @a;").Check(testkit.Rows("10 10 10")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + + 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()) + // The bindSQL has changed, the previous cache is invalid. + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) + 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)) + // We can use the new binding. + c.Assert(res.Rows()[0][0], Matches, ".*IndexReader.*") + c.Assert(res.Rows()[1][0], Matches, ".*IndexFullScan.*") + 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()) + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) + + 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").Sort().Check(testkit.Rows("1", "2")) + 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) +} +>>>>>>> f7cc15fe6... executor: fix CTE bug when used with Apply (#31256) diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index 6db52285b6953..d13bea1b1fa5d 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -4372,6 +4372,7 @@ func (b *PlanBuilder) buildProjUponView(ctx context.Context, dbName model.CIStr, // every row from outerPlan and the whole innerPlan. func (b *PlanBuilder) buildApplyWithJoinType(outerPlan, innerPlan LogicalPlan, tp JoinType) LogicalPlan { b.optFlag = b.optFlag | flagPredicatePushDown | flagBuildKeyInfo | flagDecorrelate + setIsInApplyForCTE(innerPlan) ap := LogicalApply{LogicalJoin: LogicalJoin{JoinType: tp}}.Init(b.ctx, b.getSelectOffset()) ap.SetChildren(outerPlan, innerPlan) ap.names = make([]*types.FieldName, outerPlan.Schema().Len()+innerPlan.Schema().Len()) @@ -4397,12 +4398,31 @@ func (b *PlanBuilder) buildSemiApply(outerPlan, innerPlan LogicalPlan, condition return nil, err } + setIsInApplyForCTE(innerPlan) ap := &LogicalApply{LogicalJoin: *join} ap.tp = plancodec.TypeApply ap.self = ap return ap, nil } +// setIsInApplyForCTE indicates CTE is the in inner side of Apply, +// the storage of cte needs to be reset for each outer row. +// It's better to handle this in CTEExec.Close(), but cte storage is closed when SQL is finished. +func setIsInApplyForCTE(p LogicalPlan) { + switch x := p.(type) { + case *LogicalCTE: + x.cte.IsInApply = true + setIsInApplyForCTE(x.cte.seedPartLogicalPlan) + if x.cte.recursivePartLogicalPlan != nil { + setIsInApplyForCTE(x.cte.recursivePartLogicalPlan) + } + default: + for _, child := range p.Children() { + setIsInApplyForCTE(child) + } + } +} + func (b *PlanBuilder) buildMaxOneRow(p LogicalPlan) LogicalPlan { maxOneRow := LogicalMaxOneRow{}.Init(b.ctx, b.getSelectOffset()) maxOneRow.SetChildren(p) diff --git a/planner/core/logical_plans.go b/planner/core/logical_plans.go index 77454969fbd87..e5fbe94b100a4 100644 --- a/planner/core/logical_plans.go +++ b/planner/core/logical_plans.go @@ -1191,10 +1191,11 @@ type CTEClass struct { // storageID for this CTE. IDForStorage int // optFlag is the optFlag for the whole CTE. - optFlag uint64 - HasLimit bool - LimitBeg uint64 - LimitEnd uint64 + optFlag uint64 + HasLimit bool + LimitBeg uint64 + LimitEnd uint64 + IsInApply bool } // LogicalCTE is for CTE.