From 091ae3bde0a54b126f2f2ed275dd20b0986d74d3 Mon Sep 17 00:00:00 2001 From: Chengpeng Yan <41809508+Reminiscent@users.noreply.github.com> Date: Wed, 29 Sep 2021 16:38:47 +0800 Subject: [PATCH] planner: generate addition selection when plan cache enable (#28457) --- executor/explainfor_test.go | 215 ++++++++++++++++++++++- executor/prepared_test.go | 11 +- executor/testdata/prepare_suite_out.json | 23 +-- planner/core/find_best_task.go | 27 +++ planner/core/prepare_test.go | 2 +- 5 files changed, 258 insertions(+), 20 deletions(-) diff --git a/executor/explainfor_test.go b/executor/explainfor_test.go index 7be6a60d3228f..fcfa5ba9cc939 100644 --- a/executor/explainfor_test.go +++ b/executor/explainfor_test.go @@ -557,14 +557,219 @@ func (s *testPrepareSerialSuite) TestExpressionIndexPreparePlanCache(c *C) { 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()[2][3], Matches, ".*expression_index.*") - c.Assert(res.Rows()[2][4], Matches, ".*[123,123].*") + c.Assert(len(res.Rows()), Equals, 5) + c.Assert(res.Rows()[3][3], Matches, ".*expression_index.*") + c.Assert(res.Rows()[3][4], Matches, ".*[123,123].*") tk.MustExec("set @a = 1234") tk.MustExec("execute stmt using @a") res = tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) + c.Assert(len(res.Rows()), Equals, 5) + c.Assert(res.Rows()[3][3], Matches, ".*expression_index.*") + c.Assert(res.Rows()[3][4], Matches, ".*[1234,1234].*") +} + +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()[1][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()[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 -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, 4) - c.Assert(res.Rows()[2][3], Matches, ".*expression_index.*") - c.Assert(res.Rows()[2][4], Matches, ".*[1234,1234].*") + 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), or(and(ge(test.t.a, 0), le(test.t.a, 2)), lt(test.t.a, 2))") + c.Assert(res.Rows()[3][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, 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), or(and(ge(test.t.a, 2), le(test.t.a, 1)), lt(test.t.a, 1))") + c.Assert(res.Rows()[3][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 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, ".*IndexLookUp.*") + c.Assert(res.Rows()[2][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[3][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, 6) + c.Assert(res.Rows()[1][0], Matches, ".*IndexLookUp.*") + c.Assert(res.Rows()[2][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[3][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, 5) + c.Assert(res.Rows()[1][0], Matches, ".*IndexLookUp.*") + c.Assert(res.Rows()[2][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, 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, 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), or(and(ge(test.t.a, 0), le(test.t.a, 2)), lt(test.t.a, 2))") + c.Assert(res.Rows()[3][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, 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), or(and(ge(test.t.a, 2), le(test.t.a, 1)), lt(test.t.a, 1))") + c.Assert(res.Rows()[3][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, 3) + c.Assert(res.Rows()[0][0], Matches, ".*TableReader.*") + c.Assert(res.Rows()[1][0], Matches, ".*Selection.*") + // The duplicate expressions can not be eliminated because the conditions are not the same. + // So this may be a bad case in some situations. + c.Assert(res.Rows()[1][4], Equals, "eq(test.t.d, 5), or(and(gt(test.t.a, 1), and(lt(test.t.a, 5), gt(test.t.b, 2))), and(gt(test.t.a, 8), and(lt(test.t.a, 10), gt(test.t.c, 3)))), or(and(gt(test.t.a, 1), lt(test.t.a, 5)), and(gt(test.t.a, 8), lt(test.t.a, 10)))") + c.Assert(res.Rows()[2][0], Matches, ".*TableRangeScan.*") } diff --git a/executor/prepared_test.go b/executor/prepared_test.go index d6d3e778af77c..be4416b371452 100644 --- a/executor/prepared_test.go +++ b/executor/prepared_test.go @@ -613,6 +613,7 @@ func (s *testSerialSuite) TestIssue28064(c *C) { "`d` decimal(10,0) DEFAULT NULL," + "KEY `iabc` (`a`,`b`,`c`));") tk.MustExec("set @a='123', @b='234', @c='345';") + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") tk.MustExec("prepare stmt1 from 'select * from t28064 use index (iabc) where a = ? and b = ? and c = ?';") tk.MustExec("execute stmt1 using @a, @b, @c;") @@ -620,8 +621,9 @@ func (s *testSerialSuite) TestIssue28064(c *C) { ps := []*util.ProcessInfo{tkProcess} tk.Se.SetSessionManager(&mockSessionManager1{PS: ps}) rows := tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)) - rows.Check(testkit.Rows("IndexLookUp_7 0.00 root ", - "├─IndexRangeScan_5(Build) 0.00 cop[tikv] table:t28064, index:iabc(a, b, c) range:[123 234 345,123 234 345], keep order:false, stats:pseudo", + rows.Check(testkit.Rows("IndexLookUp_8 0.00 root ", + "├─Selection_7(Build) 0.00 cop[tikv] eq(test.t28064.a, 123), eq(test.t28064.b, 234), eq(test.t28064.c, 345)", + "│ └─IndexRangeScan_5 0.00 cop[tikv] table:t28064, index:iabc(a, b, c) range:[123 234 345,123 234 345], keep order:false, stats:pseudo", "└─TableRowIDScan_6(Probe) 0.00 cop[tikv] table:t28064 keep order:false, stats:pseudo")) tk.MustExec("execute stmt1 using @a, @b, @c;") @@ -630,7 +632,8 @@ func (s *testSerialSuite) TestIssue28064(c *C) { tk.MustExec("execute stmt1 using @a, @b, @c;") rows = tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)) - rows.Check(testkit.Rows("IndexLookUp_7 0.00 root ", - "├─IndexRangeScan_5(Build) 0.00 cop[tikv] table:t28064, index:iabc(a, b, c) range:[123 234 345,123 234 345], keep order:false, stats:pseudo", + rows.Check(testkit.Rows("IndexLookUp_8 0.00 root ", + "├─Selection_7(Build) 0.00 cop[tikv] eq(test.t28064.a, 123), eq(test.t28064.b, 234), eq(test.t28064.c, 345)", + "│ └─IndexRangeScan_5 0.00 cop[tikv] table:t28064, index:iabc(a, b, c) range:[123 234 345,123 234 345], keep order:false, stats:pseudo", "└─TableRowIDScan_6(Probe) 0.00 cop[tikv] table:t28064 keep order:false, stats:pseudo")) } diff --git a/executor/testdata/prepare_suite_out.json b/executor/testdata/prepare_suite_out.json index bdcf5ede3d6c3..832010e69fde4 100644 --- a/executor/testdata/prepare_suite_out.json +++ b/executor/testdata/prepare_suite_out.json @@ -83,8 +83,9 @@ ], "Plan": [ "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:[3,3], keep order:false, stats:pseudo" + "└─IndexReader_7 0.00 root index:Selection_6", + " └─Selection_6 0.00 cop[tikv] eq(test.t1.b, 3)", + " └─IndexRangeScan_5 10.00 cop[tikv] table:t1, index:b(b, a) range:[3,3], keep order:false, stats:pseudo" ], "LastPlanUseCache": "0", "Result": [ @@ -101,8 +102,9 @@ ], "Plan": [ "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:[2,2], keep order:false, stats:pseudo" + "└─IndexReader_7 0.00 root index:Selection_6", + " └─Selection_6 0.00 cop[tikv] eq(test.t1.b, 2)", + " └─IndexRangeScan_5 10.00 cop[tikv] table:t1, index:b(b, a) range:[2,2], keep order:false, stats:pseudo" ], "LastPlanUseCache": "1", "Result": [ @@ -119,8 +121,9 @@ ], "Plan": [ "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:[-200,-200], keep order:false, stats:pseudo" + "└─IndexReader_7 0.00 root index:Selection_6", + " └─Selection_6 0.00 cop[tikv] eq(test.t1.b, -200)", + " └─IndexRangeScan_5 10.00 cop[tikv] table:t1, index:b(b, a) range:[-200,-200], keep order:false, stats:pseudo" ], "LastPlanUseCache": "1", "Result": null @@ -158,11 +161,11 @@ "Plan": [ "HashJoin_38 6387.21 root inner join, equal:[eq(test.t1.b, test.t2.b) eq(test.t1.a, test.t2.a)]", "├─IndexLookUp_63(Build) 99.80 root ", - "│ ├─Selection_62(Build) 99.80 cop[tikv] not(isnull(test.t2.b))", + "│ ├─Selection_62(Build) 99.80 cop[tikv] eq(test.t2.b, 1), not(isnull(test.t2.a)), not(isnull(test.t2.b))", "│ │ └─IndexRangeScan_60 99.90 cop[tikv] table:t2, index:b(b, a) range:[1 -inf,1 +inf], keep order:false, stats:pseudo", "│ └─TableRowIDScan_61(Probe) 99.80 cop[tikv] table:t2 keep order:false, stats:pseudo", "└─IndexLookUp_56(Probe) 99.80 root ", - " ├─Selection_55(Build) 99.80 cop[tikv] not(isnull(test.t1.b))", + " ├─Selection_55(Build) 99.80 cop[tikv] eq(test.t1.b, 1), not(isnull(test.t1.a)), not(isnull(test.t1.b))", " │ └─IndexRangeScan_53 99.90 cop[tikv] table:t1, index:b(b, a) range:[1 -inf,1 +inf], keep order:false, stats:pseudo", " └─TableRowIDScan_54(Probe) 99.80 cop[tikv] table:t1 keep order:false, stats:pseudo" ], @@ -180,11 +183,11 @@ "Plan": [ "HashJoin_38 6387.21 root inner join, equal:[eq(test.t1.b, test.t2.b) eq(test.t1.a, test.t2.a)]", "├─IndexLookUp_63(Build) 99.80 root ", - "│ ├─Selection_62(Build) 99.80 cop[tikv] not(isnull(test.t2.b))", + "│ ├─Selection_62(Build) 99.80 cop[tikv] eq(test.t2.b, 2), not(isnull(test.t2.a)), not(isnull(test.t2.b))", "│ │ └─IndexRangeScan_60 99.90 cop[tikv] table:t2, index:b(b, a) range:[2 -inf,2 +inf], keep order:false, stats:pseudo", "│ └─TableRowIDScan_61(Probe) 99.80 cop[tikv] table:t2 keep order:false, stats:pseudo", "└─IndexLookUp_56(Probe) 99.80 root ", - " ├─Selection_55(Build) 99.80 cop[tikv] not(isnull(test.t1.b))", + " ├─Selection_55(Build) 99.80 cop[tikv] eq(test.t1.b, 2), not(isnull(test.t1.a)), not(isnull(test.t1.b))", " │ └─IndexRangeScan_53 99.90 cop[tikv] table:t1, index:b(b, a) range:[2 -inf,2 +inf], keep order:false, stats:pseudo", " └─TableRowIDScan_54(Probe) 99.80 cop[tikv] table:t1 keep order:false, stats:pseudo" ], diff --git a/planner/core/find_best_task.go b/planner/core/find_best_task.go index 0041f1b627fbe..0edbe0b1e5fed 100644 --- a/planner/core/find_best_task.go +++ b/planner/core/find_best_task.go @@ -27,6 +27,7 @@ import ( "github.com/pingcap/tidb/kv" "github.com/pingcap/tidb/planner/property" "github.com/pingcap/tidb/planner/util" + "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/sessionctx/stmtctx" "github.com/pingcap/tidb/statistics" "github.com/pingcap/tidb/types" @@ -1342,6 +1343,7 @@ func (is *PhysicalIndexScan) initSchema(idxExprCols []*expression.Column, isDoub func (is *PhysicalIndexScan) addPushedDownSelection(copTask *copTask, p *DataSource, path *util.AccessPath, finalStats *property.StatsInfo) { // Add filter condition to table plan now. indexConds, tableConds := path.IndexFilters, path.TableFilters + indexConds = keepAccessCondsAsFilter4PlanCache(is.ctx, path.AccessConds, indexConds) tableConds, copTask.rootTaskConds = SplitSelCondsWithVirtualColumn(tableConds) @@ -1629,6 +1631,30 @@ func getMostCorrCol4Index(path *util.AccessPath, histColl *statistics.Table, thr return nil, corr } +// keepAccessCondsAsFilter4PlanCache is used to keep the access conditions as +// filter conditions when the following conditions are met: +// 1. Access conditions contains the lazy constant, see the MaybeOverOptimized4PlanCache +// function for more details. +// For example: if we have an table schema like `t(col1 int, col2 int, key idx(col1)), +// and a prepare stmt like 'select col1 from t where (col1 between $a and $b or col1 < $c) and col2 < 1;`. +// The normal plan will be `IndexReader -> Selection[col2 < 1] -> IndexRangeScan` +// when `$a < $b`, the conditions related to col1 in `Selection` will be eliminated. +// But when the `$a > $b`, we will get `IndexReader -> Selection[col2 < 1] -> IndexFullScan`. +// And the result will be wrong without conditions related to col1 in `Selection`. +// So we need to keep the `accessConditions` in `DataSource` to `Selection` when +// the `accessConditions` in `DataSource` contains the lazy constant. After use +// this function the `Selection` will be `((col1 > $a and col1 < $b) or col1 < $c)) +// and col2 < 1`. +func keepAccessCondsAsFilter4PlanCache(ctx sessionctx.Context, accessConds []expression.Expression, filterConds []expression.Expression) []expression.Expression { + if expression.MaybeOverOptimized4PlanCache(ctx, accessConds) { + additionFilterConds := make([]expression.Expression, len(accessConds)) + copy(additionFilterConds, accessConds) + filterConds = append(filterConds, additionFilterConds...) + filterConds = expression.RemoveDupExprs(ctx, filterConds) + } + return filterConds +} + // GetPhysicalScan returns PhysicalTableScan for the LogicalTableScan. func (s *LogicalTableScan) GetPhysicalScan(schema *expression.Schema, stats *property.StatsInfo) *PhysicalTableScan { ds := s.Source @@ -1956,6 +1982,7 @@ func (ts *PhysicalTableScan) addPushedDownSelectionToMppTask(mpp *mppTask, stats } func (ts *PhysicalTableScan) addPushedDownSelection(copTask *copTask, stats *property.StatsInfo) { + ts.filterCondition = keepAccessCondsAsFilter4PlanCache(ts.ctx, ts.AccessCondition, ts.filterCondition) ts.filterCondition, copTask.rootTaskConds = SplitSelCondsWithVirtualColumn(ts.filterCondition) var newRootConds []expression.Expression ts.filterCondition, newRootConds = expression.PushDownExprs(ts.ctx.GetSessionVars().StmtCtx, ts.filterCondition, ts.ctx.GetClient(), ts.StoreType) diff --git a/planner/core/prepare_test.go b/planner/core/prepare_test.go index 3cdb0d7ebf16d..35d9aebc8d630 100644 --- a/planner/core/prepare_test.go +++ b/planner/core/prepare_test.go @@ -191,7 +191,7 @@ func (s *testPlanSerialSuite) TestPrepareCacheDeferredFunction(c *C) { tk.MustExec("prepare sel1 from 'select id, c1 from t1 where c1 < now(3)'") sql1 := "execute sel1" - expectedPattern := `IndexReader\(Index\(t1.idx1\)\[\[-inf,[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1]) (2[0-3]|[01][0-9]):[0-5][0-9]:[0-5][0-9].[0-9][0-9][0-9]\)\]\)` + expectedPattern := `IndexReader\(Index\(t1.idx1\)\[\[-inf,[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1]) (2[0-3]|[01][0-9]):[0-5][0-9]:[0-5][0-9].[0-9][0-9][0-9]\)\]->Sel\(\[lt\(test.t1.c1, now\(3\)\)\]\)\)` var cnt [2]float64 var planStr [2]string