Skip to content

Commit

Permalink
planner: fix should not use point get plan (#21124) (#21244)
Browse files Browse the repository at this point in the history
Signed-off-by: ti-srebot <[email protected]>
  • Loading branch information
ti-srebot authored Nov 25, 2020
1 parent d7e2b47 commit beb0862
Show file tree
Hide file tree
Showing 4 changed files with 234 additions and 0 deletions.
49 changes: 49 additions & 0 deletions planner/core/point_get_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/pingcap/parser/mysql"
"github.com/pingcap/parser/opcode"
"github.com/pingcap/parser/terror"
ptypes "github.com/pingcap/parser/types"
"github.com/pingcap/tidb/expression"
"github.com/pingcap/tidb/infoschema"
"github.com/pingcap/tidb/kv"
Expand Down Expand Up @@ -464,6 +465,9 @@ func newBatchPointGetPlan(
if d.IsNull() {
return nil
}
if !checkCanConvertInPointGet(handleCol, d) {
return nil
}
intDatum, err := d.ConvertTo(ctx.GetSessionVars().StmtCtx, &handleCol.FieldType)
if err != nil {
return nil
Expand All @@ -486,6 +490,14 @@ func newBatchPointGetPlan(
// The columns in where clause should be covered by unique index
var matchIdxInfo *model.IndexInfo
permutations := make([]int, len(whereColNames))
colInfos := make([]*model.ColumnInfo, len(whereColNames))
for i, innerCol := range whereColNames {
for _, col := range tbl.Columns {
if col.Name.L == innerCol {
colInfos[i] = col
}
}
}
for _, idxInfo := range tbl.Indices {
if !idxInfo.Unique || idxInfo.State != model.StatePublic {
continue
Expand Down Expand Up @@ -538,17 +550,29 @@ func newBatchPointGetPlan(
permIndex := permutations[index]
switch innerX := inner.(type) {
case *driver.ValueExpr:
if !checkCanConvertInPointGet(colInfos[index], innerX.Datum) {
return nil
}
values[permIndex] = innerX.Datum
case *driver.ParamMarkerExpr:
if !checkCanConvertInPointGet(colInfos[index], innerX.Datum) {
return nil
}
values[permIndex] = innerX.Datum
valuesParams[permIndex] = innerX
default:
return nil
}
}
case *driver.ValueExpr:
if !checkCanConvertInPointGet(colInfos[0], x.Datum) {
return nil
}
values = []types.Datum{x.Datum}
case *driver.ParamMarkerExpr:
if !checkCanConvertInPointGet(colInfos[0], x.Datum) {
return nil
}
values = []types.Datum{x.Datum}
valuesParams = []*driver.ParamMarkerExpr{x}
default:
Expand Down Expand Up @@ -964,6 +988,9 @@ func getNameValuePairs(stmtCtx *stmtctx.StatementContext, tbl *model.TableInfo,
(col.Tp == mysql.TypeString && col.Collate == charset.CollationBin) { // This type we needn't to pad `\0` in here.
return append(nvPairs, nameValuePair{colName: colName.Name.Name.L, value: d, param: param}), false
}
if !checkCanConvertInPointGet(col, d) {
return nil, false
}
dVal, err := d.ConvertTo(stmtCtx, &col.FieldType)
if err != nil {
if terror.ErrorEqual(types.ErrOverflow, err) {
Expand All @@ -987,6 +1014,28 @@ func getNameValuePairs(stmtCtx *stmtctx.StatementContext, tbl *model.TableInfo,
return nil, false
}

func checkCanConvertInPointGet(col *model.ColumnInfo, d types.Datum) bool {
kind := d.Kind()
switch col.FieldType.EvalType() {
case ptypes.ETString:
switch kind {
case types.KindInt64, types.KindUint64,
types.KindFloat32, types.KindFloat64, types.KindMysqlDecimal:
// column type is String and constant type is numeric
return false
}
}
switch col.FieldType.Tp {
case mysql.TypeBit:
switch kind {
case types.KindString:
// column type is Bit and constant type is string
return false
}
}
return true
}

func findPKHandle(tblInfo *model.TableInfo, pairs []nameValuePair) (handlePair nameValuePair, fieldType *types.FieldType) {
if !tblInfo.PKIsHandle {
rowIDIdx := findInPairs("_tidb_rowid", pairs)
Expand Down
36 changes: 36 additions & 0 deletions planner/core/point_get_plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,7 @@ func (s *testPointGetSuite) TestUpdateWithTableReadLockWillFail(c *C) {
func (s *testPointGetSuite) TestSelectInMultiColumns(c *C) {
tk := testkit.NewTestKit(c, s.store)
tk.MustExec("use test")
tk.MustExec("drop table if exists t2")
tk.MustExec("create table t2(a int, b int, c int, primary key(a, b, c));")
tk.MustExec("insert into t2 values (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4)")
tk.MustQuery("select * from t2 where (a, b, c) in ((1, 1, 1));").Check(testkit.Rows("1 1 1"))
Expand All @@ -486,3 +487,38 @@ func (s *testPointGetSuite) TestSelectInMultiColumns(c *C) {
c.Assert(err, NotNil)
c.Assert(err.Error(), Equals, "[expression:1241]Operand should contain 3 column(s)")
}

func (s *testPointGetSuite) TestCBOShouldNotUsePointGet(c *C) {
tk := testkit.NewTestKit(c, s.store)
tk.MustExec("use test")
tk.MustExec("drop tables if exists t1, t2, t3, t4, t5")
tk.MustExec("create table t1(id varchar(20) primary key)")
tk.MustExec("create table t2(id varchar(20), unique(id))")
tk.MustExec("create table t3(id varchar(20), d varchar(20), unique(id, d))")
tk.MustExec("create table t4(id int, d varchar(20), c varchar(20), unique(id, d))")
tk.MustExec("create table t5(id bit(64) primary key)")
tk.MustExec("insert into t1 values('asdf'), ('1asdf')")
tk.MustExec("insert into t2 values('asdf'), ('1asdf')")
tk.MustExec("insert into t3 values('asdf', 't'), ('1asdf', 't')")
tk.MustExec("insert into t4 values(1, 'b', 'asdf'), (1, 'c', 'jkl'), (1, 'd', '1jkl')")
tk.MustExec("insert into t5 values(48)")

var input []string
var output []struct {
SQL string
Plan []string
Res []string
}
s.testData.GetTestCases(c, &input, &output)
for i, sql := range input {
plan := tk.MustQuery("explain " + sql)
res := tk.MustQuery(sql)
s.testData.OnRecord(func() {
output[i].SQL = sql
output[i].Plan = s.testData.ConvertRowsToStrings(plan.Rows())
output[i].Res = s.testData.ConvertRowsToStrings(res.Rows())
})
plan.Check(testkit.Rows(output[i].Plan...))
res.Check(testkit.Rows(output[i].Res...))
}
}
17 changes: 17 additions & 0 deletions planner/core/testdata/point_get_plan_in.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,22 @@
"select * from t t1 join t t2 on t1.a = t2.a where t1.a = '4' and (t2.b, t2.c) in ((1,1),(2,2))",
"select * from t where (t.b, t.c) in ((2,2), (3,3), (4,4)) order by t.b, t.c"
]
},
{
"name": "TestCBOShouldNotUsePointGet",
"cases": [
"select * from t1 where id = 0",
"select * from t1 where id = x'00'",
"select * from t1 where id = b'00'",
"select * from t1 where id = 0.0",
"select * from t1 where id = 1.0",
"select * from t1 where id in (0, 1)",
"select * from t2 where id = 0",
"select * from t2 where id in (0, 1)",
"select * from t3 where (id, d) in ((0, 't'), (1, 't'))",
"select * from t4 where (id, d, c) in ((1, 'b', 0))",
"select * from t4 where (id, d, c) in ((1, 0, 0))",
"select * from t5 where id in ('0')"
]
}
]
132 changes: 132 additions & 0 deletions planner/core/testdata/point_get_plan_out.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,137 @@
]
}
]
},
{
"Name": "TestCBOShouldNotUsePointGet",
"Cases": [
{
"SQL": "select * from t1 where id = 0",
"Plan": [
"IndexReader_10 8000.00 root index:Selection_9",
"└─Selection_9 8000.00 cop[tikv] eq(cast(test.t1.id), 0)",
" └─IndexFullScan_8 10000.00 cop[tikv] table:t1, index:PRIMARY(id) keep order:false, stats:pseudo"
],
"Res": [
"asdf"
]
},
{
"SQL": "select * from t1 where id = x'00'",
"Plan": [
"Point_Get_1 1.00 root table:t1, index:PRIMARY(id) "
],
"Res": []
},
{
"SQL": "select * from t1 where id = b'00'",
"Plan": [
"Point_Get_1 1.00 root table:t1, index:PRIMARY(id) "
],
"Res": []
},
{
"SQL": "select * from t1 where id = 0.0",
"Plan": [
"IndexReader_10 8000.00 root index:Selection_9",
"└─Selection_9 8000.00 cop[tikv] eq(cast(test.t1.id), 0)",
" └─IndexFullScan_8 10000.00 cop[tikv] table:t1, index:PRIMARY(id) keep order:false, stats:pseudo"
],
"Res": [
"asdf"
]
},
{
"SQL": "select * from t1 where id = 1.0",
"Plan": [
"IndexReader_10 8000.00 root index:Selection_9",
"└─Selection_9 8000.00 cop[tikv] eq(cast(test.t1.id), 1)",
" └─IndexFullScan_8 10000.00 cop[tikv] table:t1, index:PRIMARY(id) keep order:false, stats:pseudo"
],
"Res": [
"1asdf"
]
},
{
"SQL": "select * from t1 where id in (0, 1)",
"Plan": [
"IndexReader_10 8000.00 root index:Selection_9",
"└─Selection_9 8000.00 cop[tikv] or(eq(cast(test.t1.id), 0), eq(cast(test.t1.id), 1))",
" └─IndexFullScan_8 10000.00 cop[tikv] table:t1, index:PRIMARY(id) keep order:false, stats:pseudo"
],
"Res": [
"1asdf",
"asdf"
]
},
{
"SQL": "select * from t2 where id = 0",
"Plan": [
"IndexReader_10 8000.00 root index:Selection_9",
"└─Selection_9 8000.00 cop[tikv] eq(cast(test.t2.id), 0)",
" └─IndexFullScan_8 10000.00 cop[tikv] table:t2, index:id(id) keep order:false, stats:pseudo"
],
"Res": [
"asdf"
]
},
{
"SQL": "select * from t2 where id in (0, 1)",
"Plan": [
"IndexReader_10 8000.00 root index:Selection_9",
"└─Selection_9 8000.00 cop[tikv] or(eq(cast(test.t2.id), 0), eq(cast(test.t2.id), 1))",
" └─IndexFullScan_8 10000.00 cop[tikv] table:t2, index:id(id) keep order:false, stats:pseudo"
],
"Res": [
"1asdf",
"asdf"
]
},
{
"SQL": "select * from t3 where (id, d) in ((0, 't'), (1, 't'))",
"Plan": [
"IndexReader_10 8000.00 root index:Selection_9",
"└─Selection_9 8000.00 cop[tikv] or(and(eq(cast(test.t3.id), 0), eq(test.t3.d, \"t\")), and(eq(cast(test.t3.id), 1), eq(test.t3.d, \"t\")))",
" └─IndexFullScan_8 10000.00 cop[tikv] table:t3, index:id(id, d) keep order:false, stats:pseudo"
],
"Res": [
"1asdf t",
"asdf t"
]
},
{
"SQL": "select * from t4 where (id, d, c) in ((1, 'b', 0))",
"Plan": [
"Selection_6 0.80 root eq(cast(test.t4.c), 0)",
"└─Point_Get_5 1.00 root table:t4, index:id(id, d) "
],
"Res": [
"1 b asdf"
]
},
{
"SQL": "select * from t4 where (id, d, c) in ((1, 0, 0))",
"Plan": [
"IndexLookUp_12 8.00 root ",
"├─Selection_10(Build) 8.00 cop[tikv] eq(cast(test.t4.d), 0)",
"│ └─IndexRangeScan_8 10.00 cop[tikv] table:t4, index:id(id, d) range:[1,1], keep order:false, stats:pseudo",
"└─Selection_11(Probe) 8.00 cop[tikv] eq(cast(test.t4.c), 0)",
" └─TableRowIDScan_9 8.00 cop[tikv] table:t4 keep order:false, stats:pseudo"
],
"Res": [
"1 b asdf",
"1 c jkl"
]
},
{
"SQL": "select * from t5 where id in ('0')",
"Plan": [
"Selection_5 8000.00 root eq(test.t5.id, 0)",
"└─IndexReader_9 10000.00 root index:IndexFullScan_8",
" └─IndexFullScan_8 10000.00 cop[tikv] table:t5, index:PRIMARY(id) keep order:false, stats:pseudo"
],
"Res": []
}
]
}
]

0 comments on commit beb0862

Please sign in to comment.