Skip to content

Commit

Permalink
planner: fix the issue that planner hints don't work in some batch/po…
Browse files Browse the repository at this point in the history
…int-get plans (#23666) (#23685)
  • Loading branch information
ti-srebot authored Apr 15, 2021
1 parent 90ec27c commit 1623dcc
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 6 deletions.
51 changes: 45 additions & 6 deletions planner/core/point_get_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand Down
46 changes: 46 additions & 0 deletions planner/core/point_get_plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -599,3 +599,49 @@ func (s *testPointGetSuite) TestCBOShouldNotUsePointGet(c *C) {
res.Check(testkit.Rows(output[i].Res...))
}
}

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"))
}

0 comments on commit 1623dcc

Please sign in to comment.