diff --git a/executor/explainfor_test.go b/executor/explainfor_test.go index 72e59b732e..e2f1308c78 100644 --- a/executor/explainfor_test.go +++ b/executor/explainfor_test.go @@ -672,17 +672,18 @@ func (s *testPrepareSerialSuite) TestIssue28259(c *C) { 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(`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(len(res.Rows()), Equals, 5) c.Assert(res.Rows()[1][0], Matches, ".*IndexLookUp.*") - c.Assert(res.Rows()[2][0], Matches, ".*Selection.*") - c.Assert(res.Rows()[3][0], Matches, ".*IndexRangeScan.*") + c.Assert(res.Rows()[2][0], Matches, ".*IndexRangeScan.*") + c.Assert(res.Rows()[3][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[4][0], Matches, ".*TableRowIDScan.*") tk.MustExec("set @a=2, @b=1, @c=1;") tk.MustQuery("execute stmt using @a,@b,@c;").Check(testkit.Rows()) @@ -692,17 +693,18 @@ func (s *testPrepareSerialSuite) TestIssue28259(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, 6) + c.Assert(len(res.Rows()), Equals, 5) c.Assert(res.Rows()[1][0], Matches, ".*IndexLookUp.*") - c.Assert(res.Rows()[2][0], Matches, ".*Selection.*") - c.Assert(res.Rows()[3][0], Matches, ".*IndexFullScan.*") + c.Assert(res.Rows()[2][0], Matches, ".*IndexFullScan.*") + c.Assert(res.Rows()[3][0], Matches, ".*Selection.*") + c.Assert(res.Rows()[4][0], Matches, ".*TableRowIDScan.*") - res = tk.MustQuery("explain format = 'brief' select a from t use index(idx) " + + 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 a from t use index(idx) " + + 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.*") @@ -771,3 +773,46 @@ func (s *testPrepareSerialSuite) TestIssue28259(c *C) { 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.*") } + +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, 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.*") + + 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.*") +} diff --git a/expression/util.go b/expression/util.go index 157a298e03..6469eefbee 100644 --- a/expression/util.go +++ b/expression/util.go @@ -827,10 +827,6 @@ func RemoveDupExprs(ctx sessionctx.Context, exprs []Expression) []Expression { exists := make(map[string]struct{}, len(exprs)) sc := ctx.GetSessionVars().StmtCtx for _, expr := range exprs { - if MaybeOverOptimized4PlanCache(ctx, []Expression{expr}) { - res = append(res, expr) - continue - } key := string(expr.HashCode(sc)) if _, ok := exists[key]; !ok || IsMutableEffectsExpr(expr) { res = append(res, expr) diff --git a/planner/core/find_best_task.go b/planner/core/find_best_task.go index 66157a7a5c..ae7a59fd9f 100644 --- a/planner/core/find_best_task.go +++ b/planner/core/find_best_task.go @@ -1347,9 +1347,16 @@ 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) + if len(tableConds) > 0 { + // If we have table conditions, we should keep the addition filter conditions + // from the path's access conditions to the table filter not the index filter. + // The reason is when the index is the prefix index, it will get the wrong results. + tableConds = keepAccessCondsAsFilter4PlanCache(is.ctx, path.AccessConds, tableConds) + } else { + indexConds = keepAccessCondsAsFilter4PlanCache(is.ctx, path.AccessConds, indexConds) + } var newRootConds []expression.Expression indexConds, newRootConds = expression.PushDownExprs(is.ctx.GetSessionVars().StmtCtx, indexConds, is.ctx.GetClient(), kv.TiKV) @@ -1986,8 +1993,8 @@ 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) + ts.filterCondition = keepAccessCondsAsFilter4PlanCache(ts.ctx, ts.AccessCondition, ts.filterCondition) var newRootConds []expression.Expression ts.filterCondition, newRootConds = expression.PushDownExprs(ts.ctx.GetSessionVars().StmtCtx, ts.filterCondition, ts.ctx.GetClient(), ts.StoreType) copTask.rootTaskConds = append(copTask.rootTaskConds, newRootConds...)