From 161ea67b914fce13912bf082f97e6e4fa8b421fd Mon Sep 17 00:00:00 2001 From: Yuanjia Zhang Date: Tue, 30 Mar 2021 17:46:14 +0800 Subject: [PATCH] cherry pick #23666 to release-5.0 Signed-off-by: ti-srebot --- planner/core/point_get_plan.go | 51 +++++++++++++++--- planner/core/point_get_plan_test.go | 82 +++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 6 deletions(-) diff --git a/planner/core/point_get_plan.go b/planner/core/point_get_plan.go index aa0b48df459b5..0e2c051d100e1 100644 --- a/planner/core/point_get_plan.go +++ b/planner/core/point_get_plan.go @@ -497,7 +497,7 @@ func getLockWaitTime(ctx sessionctx.Context, lockInfo *ast.SelectLockInfo) (lock func newBatchPointGetPlan( ctx sessionctx.Context, patternInExpr *ast.PatternInExpr, handleCol *model.ColumnInfo, tbl *model.TableInfo, schema *expression.Schema, - names []*types.FieldName, whereColNames []string, + names []*types.FieldName, whereColNames []string, indexHints []*ast.IndexHint, ) *BatchPointGetPlan { statsInfo := &property.StatsInfo{RowCount: float64(len(patternInExpr.List))} var partitionColName *ast.ColumnName @@ -563,7 +563,8 @@ func newBatchPointGetPlan( } } for _, idxInfo := range tbl.Indices { - if !idxInfo.Unique || idxInfo.State != model.StatePublic || idxInfo.Invisible { + if !idxInfo.Unique || idxInfo.State != model.StatePublic || idxInfo.Invisible || + !indexIsAvailableByHints(idxInfo, indexHints) { continue } if len(idxInfo.Columns) != len(whereColNames) || idxInfo.HasPrefixIndex() { @@ -742,7 +743,7 @@ func tryWhereIn2BatchPointGet(ctx sessionctx.Context, selStmt *ast.SelectStmt) * return nil } - p := newBatchPointGetPlan(ctx, in, handleCol, tbl, schema, names, whereColNames) + p := newBatchPointGetPlan(ctx, in, handleCol, tbl, schema, names, whereColNames, tblName.IndexHints) if p == nil { return nil } @@ -824,7 +825,7 @@ func tryPointGetPlan(ctx sessionctx.Context, selStmt *ast.SelectStmt, check bool } handlePair, fieldType := findPKHandle(tbl, pairs) - if handlePair.value.Kind() != types.KindNull && len(pairs) == 1 { + if handlePair.value.Kind() != types.KindNull && len(pairs) == 1 && indexIsAvailableByHints(nil, tblName.IndexHints) { if isTableDual { p := newPointGetPlan(ctx, tblName.Schema.O, schema, tbl, names) p.IsTableDual = true @@ -846,7 +847,8 @@ func tryPointGetPlan(ctx sessionctx.Context, selStmt *ast.SelectStmt, check bool var err error for _, idxInfo := range tbl.Indices { - if !idxInfo.Unique || idxInfo.State != model.StatePublic || idxInfo.Invisible { + if !idxInfo.Unique || idxInfo.State != model.StatePublic || idxInfo.Invisible || + !indexIsAvailableByHints(idxInfo, tblName.IndexHints) { continue } if isTableDual { @@ -866,7 +868,6 @@ func tryPointGetPlan(ctx sessionctx.Context, selStmt *ast.SelectStmt, check bool p.IsTableDual = true return p } - idxValues, idxValueParams := getIndexValues(idxInfo, pairs) if idxValues == nil { continue @@ -896,6 +897,44 @@ func tryPointGetPlan(ctx sessionctx.Context, selStmt *ast.SelectStmt, check bool return nil } +// indexIsAvailableByHints checks whether this index is filtered by these specified index hints. +// idxInfo is PK if it's nil +func indexIsAvailableByHints(idxInfo *model.IndexInfo, idxHints []*ast.IndexHint) bool { + if len(idxHints) == 0 { + return true + } + match := func(name model.CIStr) bool { + if idxInfo == nil { + return name.L == "primary" + } + return idxInfo.Name.L == name.L + } + // NOTICE: it's supposed that ignore hints and use/force hints will not be applied together since the effect of + // the former will be eliminated by the latter. + isIgnore := false + for _, hint := range idxHints { + if hint.HintScope != ast.HintForScan { + continue + } + if hint.HintType == ast.HintIgnore && hint.IndexNames != nil { + isIgnore = true + for _, name := range hint.IndexNames { + if match(name) { + return false + } + } + } + if (hint.HintType == ast.HintForce || hint.HintType == ast.HintUse) && hint.IndexNames != nil { + for _, name := range hint.IndexNames { + if match(name) { + return true + } + } + } + } + return isIgnore +} + func partitionNameInSet(name model.CIStr, pnames []model.CIStr) bool { for _, pname := range pnames { // Case insensitive, create table partition p0, query using P0 is OK. diff --git a/planner/core/point_get_plan_test.go b/planner/core/point_get_plan_test.go index bc331441e139d..712ddb2560e1c 100644 --- a/planner/core/point_get_plan_test.go +++ b/planner/core/point_get_plan_test.go @@ -599,3 +599,85 @@ func (s *testPointGetSuite) TestCBOShouldNotUsePointGet(c *C) { res.Check(testkit.Rows(output[i].Res...)) } } +<<<<<<< HEAD +======= + +func (s *testPointGetSuite) TestPointGetWithIndexHints(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + // point get + tk.MustExec("create table t(a int, b int, unique index ab(a, b), unique index ba(b, a))") + tk.MustQuery("explain format='brief' select a, b from t where a=1 and b=1").Check(testkit.Rows("Point_Get 1.00 root table:t, index:ab(a, b) ")) + tk.MustQuery("explain format='brief' select a, b from t use index(ba) where a=1 and b=1").Check(testkit.Rows("Point_Get 1.00 root table:t, index:ba(b, a) ")) + tk.MustQuery("explain format='brief' select a, b from t ignore index(ab, ba) where a=1 and b=1").Check(testkit.Rows( + "TableReader 1.00 root data:Selection", + "└─Selection 1.00 cop[tikv] eq(test.t.a, 1), eq(test.t.b, 1)", + " └─TableFullScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo")) + + // batch get + tk.MustQuery("explain format='brief' select a, b from t where (a=1 and b=1) or (a=2 and b=2)").Check(testkit.Rows("Batch_Point_Get 2.00 root table:t, index:ab(a, b) keep order:false, desc:false")) + tk.MustQuery("explain format='brief' select a, b from t use index(ba) where (a=1 and b=1) or (a=2 and b=2)").Check(testkit.Rows("Batch_Point_Get 2.00 root table:t, index:ba(b, a) keep order:false, desc:false")) + tk.MustQuery("explain format='brief' select a, b from t ignore index(ab, ba) where (a=1 and b=1) or (a=2 and b=2)").Check(testkit.Rows( + "TableReader 2.00 root data:Selection", + "└─Selection 2.00 cop[tikv] or(and(eq(test.t.a, 1), eq(test.t.b, 1)), and(eq(test.t.a, 2), eq(test.t.b, 2)))", + " └─TableFullScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo")) + tk.MustQuery("explain format='brief' select a, b from t where (a, b) in ((1, 1), (2, 2))").Check(testkit.Rows("Batch_Point_Get 2.00 root table:t, index:ab(a, b) keep order:false, desc:false")) + tk.MustQuery("explain format='brief' select a, b from t use index(ba) where (a, b) in ((1, 1), (2, 2))").Check(testkit.Rows("Batch_Point_Get 2.00 root table:t, index:ba(b, a) keep order:false, desc:false")) + tk.MustQuery("explain format='brief' select a, b from t ignore index(ab, ba) where (a, b) in ((1, 1), (2, 2))").Check(testkit.Rows( + "TableReader 2.00 root data:Selection", + "└─Selection 2.00 cop[tikv] or(and(eq(test.t.a, 1), eq(test.t.b, 1)), and(eq(test.t.a, 2), eq(test.t.b, 2)))", + " └─TableFullScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo")) + + // primary key + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1(a int primary key, b int, unique index ab(a, b))") + tk.MustQuery("explain format='brief' select a from t1 where a=1").Check(testkit.Rows("Point_Get 1.00 root table:t1 handle:1")) + tk.MustQuery("explain format='brief' select a from t1 use index(ab) where a=1").Check(testkit.Rows( + "IndexReader 10.00 root index:IndexRangeScan", + "└─IndexRangeScan 10.00 cop[tikv] table:t1, index:ab(a, b) range:[1,1], keep order:false, stats:pseudo")) + + // other cases + tk.MustExec("drop table if exists t2") + tk.MustExec("create table t2 (a int, b int, unique index aa(a), unique index bb(b))") + tk.MustQuery("explain format='brief' select a from t2 ignore index(bb) where a=1").Check(testkit.Rows("Point_Get 1.00 root table:t2, index:aa(a) ")) + tk.MustQuery("explain format='brief' select a from t2 use index(bb) where a=1").Check(testkit.Rows( + "IndexLookUp 1.00 root ", + "├─IndexFullScan(Build) 10000.00 cop[tikv] table:t2, index:bb(b) keep order:false, stats:pseudo", + "└─Selection(Probe) 1.00 cop[tikv] eq(test.t2.a, 1)", + " └─TableRowIDScan 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo")) +} + +func (s *testPointGetSuite) TestIssue18042(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, c int, primary key(a), index ab(a, b));") + tk.MustExec("insert into t values (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4)") + tk.MustExec("SELECT /*+ MAX_EXECUTION_TIME(100), MEMORY_QUOTA(1 MB) */ * FROM t where a = 1;") + c.Assert(tk.Se.GetSessionVars().StmtCtx.MemQuotaQuery, Equals, int64(1<<20)) + c.Assert(tk.Se.GetSessionVars().StmtCtx.MaxExecutionTime, Equals, uint64(100)) + tk.MustExec("drop table t") +} + +func (s *testPointGetSuite) TestIssue23511(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2;") + tk.MustExec("CREATE TABLE `t1` (`COL1` bit(11) NOT NULL,PRIMARY KEY (`COL1`));") + tk.MustExec("CREATE TABLE `t2` (`COL1` bit(11) NOT NULL);") + tk.MustExec("insert into t1 values(b'00000111001'), (b'00000000000');") + tk.MustExec("insert into t2 values(b'00000111001');") + tk.MustQuery("select * from t1 where col1 = 0x39;").Check(testkit.Rows("\x009")) + tk.MustQuery("select * from t2 where col1 = 0x39;").Check(testkit.Rows("\x009")) + tk.MustQuery("select * from t1 where col1 = 0x00;").Check(testkit.Rows("\x00\x00")) + tk.MustQuery("select * from t1 where col1 = 0x0000;").Check(testkit.Rows("\x00\x00")) + tk.MustQuery("explain format = 'brief' select * from t1 where col1 = 0x39;"). + Check(testkit.Rows("Point_Get 1.00 root table:t1, index:PRIMARY(COL1) ")) + tk.MustQuery("select * from t1 where col1 = 0x0039;").Check(testkit.Rows("\x009")) + tk.MustQuery("select * from t2 where col1 = 0x0039;").Check(testkit.Rows("\x009")) + tk.MustQuery("select * from t1 where col1 = 0x000039;").Check(testkit.Rows("\x009")) + tk.MustQuery("select * from t2 where col1 = 0x000039;").Check(testkit.Rows("\x009")) + tk.MustExec("drop table t1, t2;") +} +>>>>>>> 60111e1c4... planner: fix the issue that planner hints don't work in some batch/point-get plans (#23666)