Skip to content

Commit

Permalink
plan: handle type convertion when building index range. (pingcap#1964)
Browse files Browse the repository at this point in the history
  • Loading branch information
coocood authored and hanfei1991 committed Nov 7, 2016
1 parent 1eecf8e commit 6dd4571
Show file tree
Hide file tree
Showing 7 changed files with 269 additions and 65 deletions.
12 changes: 10 additions & 2 deletions mysql/charset.go
Original file line number Diff line number Diff line change
Expand Up @@ -549,8 +549,16 @@ var CollationNames = map[string]uint8{

// MySQL collation informations.
const (
DefaultCharset = "utf8"
UTF8Charset = "utf8"
UTF8MB4Charset = "utf8mb4"
DefaultCharset = UTF8Charset
DefaultCollationID = 33
BinaryCollationID = 63
DefaultCollationName = "utf8_general_ci"
UTF8DefaultCollation = "utf8_general_ci"
DefaultCollationName = UTF8DefaultCollation
)

// IsUTF8Charset checks if charset is utf8 or utf8mb4
func IsUTF8Charset(charset string) bool {
return charset == UTF8Charset || charset == UTF8MB4Charset
}
2 changes: 1 addition & 1 deletion plan/physical_plan_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ func (p *DataSource) convert2IndexScan(prop *requiredProperty, index *model.Inde
}
} else {
rb := rangeBuilder{}
is.Ranges = rb.buildIndexRanges(fullRange)
is.Ranges = rb.buildIndexRanges(fullRange, types.NewFieldType(mysql.TypeNull))
}
is.DoubleRead = !isCoveringIndex(is.Columns, is.Index.Columns, is.Table.PKIsHandle)
return resultPlan.matchProperty(prop, &physicalPlanInfo{count: rowCount}), nil
Expand Down
158 changes: 122 additions & 36 deletions plan/plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ func newLongType() types.FieldType {
return *(types.NewFieldType(mysql.TypeLong))
}

func newStringType() types.FieldType {
ft := types.NewFieldType(mysql.TypeVarchar)
ft.Charset, ft.Collate = types.DefaultCharsetForType(mysql.TypeVarchar)
return *ft
}

func mockResolve(node ast.Node) error {
indices := []*model.IndexInfo{
{
Expand All @@ -62,14 +68,17 @@ func mockResolve(node ast.Node) error {
{
Name: model.NewCIStr("c"),
Length: types.UnspecifiedLength,
Offset: 1,
},
{
Name: model.NewCIStr("d"),
Length: types.UnspecifiedLength,
Offset: 2,
},
{
Name: model.NewCIStr("e"),
Length: types.UnspecifiedLength,
Offset: 3,
},
},
State: model.StatePublic,
Expand All @@ -84,6 +93,27 @@ func mockResolve(node ast.Node) error {
},
State: model.StateWriteOnly,
},
{
Name: model.NewCIStr("c_d_e_str"),
Columns: []*model.IndexColumn{
{
Name: model.NewCIStr("c_str"),
Length: types.UnspecifiedLength,
Offset: 5,
},
{
Name: model.NewCIStr("d_str"),
Length: types.UnspecifiedLength,
Offset: 6,
},
{
Name: model.NewCIStr("e_str"),
Length: types.UnspecifiedLength,
Offset: 7,
},
},
State: model.StatePublic,
},
}
pkColumn := &model.ColumnInfo{
State: model.StatePublic,
Expand Down Expand Up @@ -115,17 +145,40 @@ func mockResolve(node ast.Node) error {
FieldType: newLongType(),
ID: 5,
}
colStr1 := &model.ColumnInfo{
State: model.StatePublic,
Name: model.NewCIStr("c_str"),
FieldType: newStringType(),
ID: 6,
}
colStr2 := &model.ColumnInfo{
State: model.StatePublic,
Name: model.NewCIStr("d_str"),
FieldType: newStringType(),
ID: 7,
}
colStr3 := &model.ColumnInfo{
State: model.StatePublic,
Name: model.NewCIStr("e_str"),
FieldType: newStringType(),
ID: 8,
}

pkColumn.Flag = mysql.PriKeyFlag
table := &model.TableInfo{
Columns: []*model.ColumnInfo{pkColumn, col0, col1, col2, col3},
Columns: []*model.ColumnInfo{pkColumn, col0, col1, col2, col3, colStr1, colStr2, colStr3},
Indices: indices,
Name: model.NewCIStr("t"),
PKIsHandle: true,
}
is := infoschema.MockInfoSchema([]*model.TableInfo{table})
ctx := mock.NewContext()
variable.BindSessionVars(ctx)
return MockResolveName(node, is, "test", ctx)
err := MockResolveName(node, is, "test", ctx)
if err != nil {
return err
}
return InferType(node)
}

func supportExpr(exprType tipb.ExprType) bool {
Expand Down Expand Up @@ -240,14 +293,14 @@ func (s *testPlanSuite) TestPushDownAggregation(c *C) {
sql: "select sum(b) from t group by c",
best: "Table(t)->HashAgg->Projection",
aggFuns: "[sum(test.t.b)]",
aggFields: "[blob decimal(0,0)]",
aggFields: "[blob decimal]",
gbyItems: "[test.t.c]",
},
{
sql: "select max(b + c), min(case when b then 1 else 2 end) from t group by d + e, a",
best: "Table(t)->HashAgg->Projection",
aggFuns: "[max(plus(test.t.b, test.t.c)) min(case(test.t.b, 1, 2))]",
aggFields: "[blob decimal(0,0) decimal(0,0)]",
aggFields: "[blob bigint bigint]",
gbyItems: "[plus(test.t.d, test.t.e) test.t.a]",
},
}
Expand Down Expand Up @@ -1055,60 +1108,94 @@ func (s *testPlanSuite) TestRefine(c *C) {
best: "Table(t)->Selection->Projection",
},
{
sql: "select a from t where c like ''",
best: "Index(t.c_d_e)[[,]]->Projection",
sql: "select a from t where c_str like ''",
best: "Index(t.c_d_e_str)[[,]]->Projection",
},
{
sql: "select a from t where c like 'abc'",
best: "Index(t.c_d_e)[[abc,abc]]->Projection",
sql: "select a from t where c_str like 'abc'",
best: "Index(t.c_d_e_str)[[abc,abc]]->Projection",
},
{
sql: "select a from t where c not like 'abc'",
sql: "select a from t where c_str not like 'abc'",
best: "Table(t)->Selection->Projection",
},
{
sql: "select a from t where not (c like 'abc' or c like 'abd')",
sql: "select a from t where not (c_str like 'abc' or c_str like 'abd')",
best: "Table(t)->Selection->Projection",
},
{
sql: "select a from t where c like '_abc'",
sql: "select a from t where c_str like '_abc'",
best: "Table(t)->Selection->Projection",
},
{
sql: "select a from t where c like 'abc%'",
best: "Index(t.c_d_e)[[abc,abd)]->Projection",
sql: "select a from t where c_str like 'abc%'",
best: "Index(t.c_d_e_str)[[abc,abd)]->Projection",
},
{
sql: "select a from t where c like 'abc_'",
best: "Index(t.c_d_e)[(abc,abd)]->Selection->Projection",
sql: "select a from t where c_str like 'abc_'",
best: "Index(t.c_d_e_str)[(abc,abd)]->Selection->Projection",
},
{
sql: "select a from t where c like 'abc%af'",
best: "Index(t.c_d_e)[[abc,abd)]->Selection->Projection",
sql: "select a from t where c_str like 'abc%af'",
best: "Index(t.c_d_e_str)[[abc,abd)]->Selection->Projection",
},
{
sql: `select a from t where c like 'abc\\_' escape ''`,
best: "Index(t.c_d_e)[[abc_,abc_]]->Projection",
sql: `select a from t where c_str like 'abc\\_' escape ''`,
best: "Index(t.c_d_e_str)[[abc_,abc_]]->Projection",
},
{
sql: `select a from t where c like 'abc\\_'`,
best: "Index(t.c_d_e)[[abc_,abc_]]->Projection",
sql: `select a from t where c_str like 'abc\\_'`,
best: "Index(t.c_d_e_str)[[abc_,abc_]]->Projection",
},
{
sql: `select a from t where c like 'abc\\\\_'`,
best: "Index(t.c_d_e)[(abc\\,abc])]->Selection->Projection",
sql: `select a from t where c_str like 'abc\\\\_'`,
best: "Index(t.c_d_e_str)[(abc\\,abc])]->Selection->Projection",
},
{
sql: `select a from t where c like 'abc\\_%'`,
best: "Index(t.c_d_e)[[abc_,abc`)]->Projection",
sql: `select a from t where c_str like 'abc\\_%'`,
best: "Index(t.c_d_e_str)[[abc_,abc`)]->Projection",
},
{
sql: `select a from t where c like 'abc=_%' escape '='`,
best: "Index(t.c_d_e)[[abc_,abc`)]->Projection",
sql: `select a from t where c_str like 'abc=_%' escape '='`,
best: "Index(t.c_d_e_str)[[abc_,abc`)]->Projection",
},
{
sql: `select a from t where c like 'abc\\__'`,
best: "Index(t.c_d_e)[(abc_,abc`)]->Selection->Projection",
sql: `select a from t where c_str like 'abc\\__'`,
best: "Index(t.c_d_e_str)[(abc_,abc`)]->Selection->Projection",
},
{
// Check that 123 is converted to string '123'. index can be used.
sql: `select a from t where c_str like 123`,
best: "Index(t.c_d_e_str)[[123,123]]->Projection",
},
{
// c is not string type, added cast to string during InferType, no index can be used.
sql: `select a from t where c like '1'`,
best: "Table(t)->Selection->Projection",
},
{
sql: `select a from t where c = 1.1 and d > 3`,
best: "Index(t.c_d_e)[]->Projection",
},
{
sql: `select a from t where c = 1.9 and d > 3`,
best: "Index(t.c_d_e)[]->Projection",
},
{
sql: `select a from t where c < 1.1`,
best: "Index(t.c_d_e)[[-inf,1]]->Projection",
},
{
sql: `select a from t where c <= 1.9`,
best: "Index(t.c_d_e)[[-inf,2)]->Projection",
},
{
sql: `select a from t where c >= 1.1`,
best: "Index(t.c_d_e)[(1,+inf]]->Projection",
},
{
sql: `select a from t where c > 1.9`,
best: "Index(t.c_d_e)[[2,+inf]]->Projection",
},
}
for _, ca := range cases {
Expand All @@ -1132,7 +1219,8 @@ func (s *testPlanSuite) TestRefine(c *C) {
c.Assert(err, IsNil)
info, err := p.convert2PhysicalPlan(&requiredProperty{})
c.Assert(err, IsNil)
c.Assert(ToString(info.p), Equals, ca.best, Commentf("for %s", ca.sql))
jsonPlan, _ := info.p.MarshalJSON()
c.Assert(ToString(info.p), Equals, ca.best, Commentf("for %s, %s", ca.sql, string(jsonPlan)))
}
}

Expand Down Expand Up @@ -1434,11 +1522,9 @@ func (s *testPlanSuite) TestNewRangeBuilder(c *C) {
}

for _, ca := range cases {
sql := "select 1 from t where " + ca.exprStr
stmts, err := s.Parse(sql, "", "")
sql := "select * from t where " + ca.exprStr
stmt, err := s.ParseOneStmt(sql, "", "")
c.Assert(err, IsNil, Commentf("error %v, for expr %s", err, ca.exprStr))
stmt := stmts[0].(*ast.SelectStmt)

err = mockResolve(stmt)
c.Assert(err, IsNil)

Expand Down Expand Up @@ -1523,7 +1609,7 @@ func (s *testPlanSuite) TestConstantFolding(c *C) {
},
{
exprStr: "a in (hex(12), 'a', '9')",
resultStr: "in(test.t.a, C, a, 9)",
resultStr: "in(test.t.a, C, 0, 9)",
},
{
exprStr: "'string' is not null",
Expand Down Expand Up @@ -1749,7 +1835,7 @@ func (s *testPlanSuite) TestConstantPropagation(c *C) {
},
{
sql: "a = b and b = c and c LIKE 'abc%'",
after: "eq(test.t.a, test.t.b), eq(test.t.b, test.t.c), like(test.t.a, abc%, 92), like(test.t.b, abc%, 92), like(test.t.c, abc%, 92)",
after: "eq(test.t.a, test.t.b), eq(test.t.b, test.t.c), like(cast(test.t.c), abc%, 92)",
},
{
sql: "a = b and a > 2 and b > 3 and a < 1 and b < 2",
Expand Down
Loading

0 comments on commit 6dd4571

Please sign in to comment.