Skip to content

Commit

Permalink
planner: support specify table partition in optimize hint (#17638)
Browse files Browse the repository at this point in the history
Co-authored-by: HuaiyuXu <[email protected]>
  • Loading branch information
lzmhhh123 and XuHuaiyu authored Jun 22, 2020
1 parent 8cd57aa commit 3e5db05
Show file tree
Hide file tree
Showing 8 changed files with 499 additions and 83 deletions.
46 changes: 46 additions & 0 deletions planner/core/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1102,6 +1102,52 @@ func (s *testIntegrationSuite) TestStreamAggProp(c *C) {
}
}

func (s *testIntegrationSuite) TestOptimizeHintOnPartitionTable(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 varchar(20),
primary key(a), key(b), key(c)
) partition by range columns(a) (
partition p0 values less than(6),
partition p1 values less than(11),
partition p2 values less than(16));`)
tk.MustExec(`insert into t values (1,1,"1"), (2,2,"2"), (8,8,"8"), (11,11,"11"), (15,15,"15")`)

// Create virtual tiflash replica info.
dom := domain.GetDomain(tk.Se)
is := dom.InfoSchema()
db, exists := is.SchemaByName(model.NewCIStr("test"))
c.Assert(exists, IsTrue)
for _, tblInfo := range db.Tables {
if tblInfo.Name.L == "t" {
tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{
Count: 1,
Available: true,
}
}
}

var input []string
var output []struct {
SQL string
Plan []string
Warn []string
}
s.testData.GetTestCases(c, &input, &output)
for i, tt := range input {
s.testData.OnRecord(func() {
output[i].SQL = tt
output[i].Plan = s.testData.ConvertRowsToStrings(tk.MustQuery("explain " + tt).Rows())
output[i].Warn = s.testData.ConvertRowsToStrings(tk.MustQuery("show warnings").Rows())
})
tk.MustQuery("explain " + tt).Check(testkit.Rows(output[i].Plan...))
tk.MustQuery("show warnings").Check(testkit.Rows(output[i].Warn...))
}
}

func (s *testIntegrationSuite) TestSelectLimit(c *C) {
tk := testkit.NewTestKit(c, s.store)

Expand Down
56 changes: 33 additions & 23 deletions planner/core/logical_plan_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -514,23 +514,27 @@ func (ds *DataSource) setPreferredStoreType(hintInfo *tableHintInfo) {
} else {
alias = &hintTableInfo{dbName: ds.DBName, tblName: ds.tableInfo.Name, selectOffset: ds.SelectBlockOffset()}
}
if hintInfo.ifPreferTiKV(alias) {
if hintTbl := hintInfo.ifPreferTiKV(alias); hintTbl != nil {
for _, path := range ds.possibleAccessPaths {
if path.StoreType == kv.TiKV {
ds.preferStoreType |= preferTiKV
ds.preferPartitions[preferTiKV] = hintTbl.partitions
break
}
}
if ds.preferStoreType == 0 {
if ds.preferStoreType&preferTiKV == 0 {
errMsg := fmt.Sprintf("No available path for table %s.%s with the store type %s of the hint /*+ read_from_storage */, "+
"please check the status of the table replica and variable value of tidb_isolation_read_engines(%v)",
ds.DBName.O, ds.table.Meta().Name.O, kv.TiKV.Name(), ds.ctx.GetSessionVars().GetIsolationReadEngines())
warning := ErrInternal.GenWithStack(errMsg)
ds.ctx.GetSessionVars().StmtCtx.AppendWarning(warning)
}
}
if hintInfo.ifPreferTiFlash(alias) {
if ds.preferStoreType != 0 {
if hintTbl := hintInfo.ifPreferTiFlash(alias); hintTbl != nil {
// 1. `ds.tableInfo.Partition == nil`, which means the hint takes effect in the whole table.
// 2. `ds.preferStoreType != 0`, which means there's a hint hit the both TiKV value and TiFlash value for table.
// If it's satisfied the above two conditions, then we can make sure there are some hints conflicted.
if ds.preferStoreType != 0 && ds.tableInfo.Partition == nil {
errMsg := fmt.Sprintf("Storage hints are conflict, you can only specify one storage type of table %s.%s",
alias.dbName.L, alias.tblName.L)
warning := ErrInternal.GenWithStack(errMsg)
Expand All @@ -541,10 +545,11 @@ func (ds *DataSource) setPreferredStoreType(hintInfo *tableHintInfo) {
for _, path := range ds.possibleAccessPaths {
if path.StoreType == kv.TiFlash {
ds.preferStoreType |= preferTiFlash
ds.preferPartitions[preferTiFlash] = hintTbl.partitions
break
}
}
if ds.preferStoreType == 0 {
if ds.preferStoreType&preferTiFlash == 0 {
errMsg := fmt.Sprintf("No available path for table %s.%s with the store type %s of the hint /*+ read_from_storage */, "+
"please check the status of the table replica and variable value of tidb_isolation_read_engines(%v)",
ds.DBName.O, ds.table.Meta().Name.O, kv.TiFlash.Name(), ds.ctx.GetSessionVars().GetIsolationReadEngines())
Expand Down Expand Up @@ -2281,15 +2286,15 @@ func (b *PlanBuilder) pushTableHints(hints []*ast.TableOptimizerHint, nodeType u

switch hint.HintName.L {
case TiDBMergeJoin, HintSMJ:
sortMergeTables = append(sortMergeTables, tableNames2HintTableInfo(b.ctx, hint.Tables, b.hintProcessor, nodeType, currentLevel)...)
sortMergeTables = append(sortMergeTables, tableNames2HintTableInfo(b.ctx, hint.HintName.L, hint.Tables, b.hintProcessor, nodeType, currentLevel)...)
case TiDBIndexNestedLoopJoin, HintINLJ:
INLJTables = append(INLJTables, tableNames2HintTableInfo(b.ctx, hint.Tables, b.hintProcessor, nodeType, currentLevel)...)
INLJTables = append(INLJTables, tableNames2HintTableInfo(b.ctx, hint.HintName.L, hint.Tables, b.hintProcessor, nodeType, currentLevel)...)
case HintINLHJ:
INLHJTables = append(INLHJTables, tableNames2HintTableInfo(b.ctx, hint.Tables, b.hintProcessor, nodeType, currentLevel)...)
INLHJTables = append(INLHJTables, tableNames2HintTableInfo(b.ctx, hint.HintName.L, hint.Tables, b.hintProcessor, nodeType, currentLevel)...)
case HintINLMJ:
INLMJTables = append(INLMJTables, tableNames2HintTableInfo(b.ctx, hint.Tables, b.hintProcessor, nodeType, currentLevel)...)
INLMJTables = append(INLMJTables, tableNames2HintTableInfo(b.ctx, hint.HintName.L, hint.Tables, b.hintProcessor, nodeType, currentLevel)...)
case TiDBHashJoin, HintHJ:
hashJoinTables = append(hashJoinTables, tableNames2HintTableInfo(b.ctx, hint.Tables, b.hintProcessor, nodeType, currentLevel)...)
hashJoinTables = append(hashJoinTables, tableNames2HintTableInfo(b.ctx, hint.HintName.L, hint.Tables, b.hintProcessor, nodeType, currentLevel)...)
case HintHashAgg:
aggHints.preferAggType |= preferHashAgg
case HintStreamAgg:
Expand All @@ -2302,8 +2307,9 @@ func (b *PlanBuilder) pushTableHints(hints []*ast.TableOptimizerHint, nodeType u
dbName = model.NewCIStr(b.ctx.GetSessionVars().CurrentDB)
}
indexHintList = append(indexHintList, indexHintInfo{
dbName: dbName,
tblName: hint.Tables[0].TableName,
dbName: dbName,
tblName: hint.Tables[0].TableName,
partitions: hint.Tables[0].PartitionList,
indexHint: &ast.IndexHint{
IndexNames: hint.Indexes,
HintType: ast.HintUse,
Expand All @@ -2316,8 +2322,9 @@ func (b *PlanBuilder) pushTableHints(hints []*ast.TableOptimizerHint, nodeType u
dbName = model.NewCIStr(b.ctx.GetSessionVars().CurrentDB)
}
indexHintList = append(indexHintList, indexHintInfo{
dbName: dbName,
tblName: hint.Tables[0].TableName,
dbName: dbName,
tblName: hint.Tables[0].TableName,
partitions: hint.Tables[0].PartitionList,
indexHint: &ast.IndexHint{
IndexNames: hint.Indexes,
HintType: ast.HintIgnore,
Expand All @@ -2327,18 +2334,19 @@ func (b *PlanBuilder) pushTableHints(hints []*ast.TableOptimizerHint, nodeType u
case HintReadFromStorage:
switch hint.HintData.(model.CIStr).L {
case HintTiFlash:
tiflashTables = append(tiflashTables, tableNames2HintTableInfo(b.ctx, hint.Tables, b.hintProcessor, nodeType, currentLevel)...)
tiflashTables = append(tiflashTables, tableNames2HintTableInfo(b.ctx, hint.HintName.L, hint.Tables, b.hintProcessor, nodeType, currentLevel)...)
case HintTiKV:
tikvTables = append(tikvTables, tableNames2HintTableInfo(b.ctx, hint.Tables, b.hintProcessor, nodeType, currentLevel)...)
tikvTables = append(tikvTables, tableNames2HintTableInfo(b.ctx, hint.HintName.L, hint.Tables, b.hintProcessor, nodeType, currentLevel)...)
}
case HintIndexMerge:
dbName := hint.Tables[0].DBName
if dbName.L == "" {
dbName = model.NewCIStr(b.ctx.GetSessionVars().CurrentDB)
}
indexMergeHintList = append(indexMergeHintList, indexHintInfo{
dbName: dbName,
tblName: hint.Tables[0].TableName,
dbName: dbName,
tblName: hint.Tables[0].TableName,
partitions: hint.Tables[0].PartitionList,
indexHint: &ast.IndexHint{
IndexNames: hint.Indexes,
HintType: ast.HintUse,
Expand Down Expand Up @@ -2710,11 +2718,11 @@ func (b *PlanBuilder) buildDataSource(ctx context.Context, tn *ast.TableName, as
if tblName.L == "" {
tblName = tn.Name
}
possiblePaths, err := b.getPossibleAccessPaths(tn.IndexHints, tbl, dbName, tblName)
possiblePaths, err := getPossibleAccessPaths(b.ctx, b.TableHints(), tn.IndexHints, tbl, dbName, tblName)
if err != nil {
return nil, err
}
possiblePaths, err = b.filterPathByIsolationRead(possiblePaths, dbName)
possiblePaths, err = filterPathByIsolationRead(b.ctx, possiblePaths, dbName)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -2755,7 +2763,7 @@ func (b *PlanBuilder) buildDataSource(ctx context.Context, tn *ast.TableName, as
}

// extract the IndexMergeHint
var indexMergeHints []*ast.IndexHint
var indexMergeHints []indexHintInfo
if hints := b.TableHints(); hints != nil {
for i, hint := range hints.indexMergeHintList {
if hint.tblName.L == tblName.L && hint.dbName.L == dbName.L {
Expand All @@ -2782,7 +2790,7 @@ func (b *PlanBuilder) buildDataSource(ctx context.Context, tn *ast.TableName, as
}
}
if len(invalidIdxNames) == 0 {
indexMergeHints = append(indexMergeHints, hint.indexHint)
indexMergeHints = append(indexMergeHints, hint)
} else {
// Append warning if there are invalid index names.
errMsg := fmt.Sprintf("use_index_merge(%s) is inapplicable, check whether the indexes (%s) "+
Expand All @@ -2799,12 +2807,14 @@ func (b *PlanBuilder) buildDataSource(ctx context.Context, tn *ast.TableName, as
table: tbl,
tableInfo: tableInfo,
statisticTable: statisticTable,
indexHints: tn.IndexHints,
astIndexHints: tn.IndexHints,
IndexHints: b.TableHints().indexHintList,
indexMergeHints: indexMergeHints,
possibleAccessPaths: possiblePaths,
Columns: make([]*model.ColumnInfo, 0, len(columns)),
partitionNames: tn.PartitionNames,
TblCols: make([]*expression.Column, 0, len(columns)),
preferPartitions: make(map[int][]model.CIStr),
}.Init(b.ctx, b.getSelectOffset())

var handleCol *expression.Column
Expand Down
17 changes: 10 additions & 7 deletions planner/core/logical_plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -466,15 +466,16 @@ type LogicalUnionScan struct {
type DataSource struct {
logicalSchemaProducer

indexHints []*ast.IndexHint
table table.Table
tableInfo *model.TableInfo
Columns []*model.ColumnInfo
DBName model.CIStr
astIndexHints []*ast.IndexHint
IndexHints []indexHintInfo
table table.Table
tableInfo *model.TableInfo
Columns []*model.ColumnInfo
DBName model.CIStr

TableAsName *model.CIStr
// indexMergeHints are the hint for indexmerge.
indexMergeHints []*ast.IndexHint
indexMergeHints []indexHintInfo
// pushedDownConds are the conditions that will be pushed down to coprocessor.
pushedDownConds []expression.Expression
// allConds contains all the filters on this table. For now it's maintained
Expand Down Expand Up @@ -504,8 +505,10 @@ type DataSource struct {
// TblColHists contains the Histogram of all original table columns,
// it is converted from statisticTable, and used for IO/network cost estimating.
TblColHists *statistics.HistColl
//preferStoreType means the DataSource is enforced to which storage.
// preferStoreType means the DataSource is enforced to which storage.
preferStoreType int
// preferPartitions store the map, the key represents store type, the value represents the partition name list.
preferPartitions map[int][]model.CIStr
}

// ExtractCorrelatedCols implements LogicalPlan interface.
Expand Down
Loading

0 comments on commit 3e5db05

Please sign in to comment.