* support batch cop for tiflash * support batch cop * support join push down to tiflash * refine * change pb * push join * fix * add hint * refine hint * add ranges * fix * fix * fix push down * fix index * enable distsql for join * add a session var to disable/enable broadcast join * fix bug * fix bug * tiny fix * enable cast decimal pushdown to tiflash * fix * fix bc join bug * make broadcast plan stable * refine code * fix bug * basic support for multi table broadcast join * fix bug * basic cbo for broadcast join * improve * fix bug * remote useless code * add tests * pass unit tests * refine code * support execute summary info for broadcast join * fix bug in explain for broadcast join * format code * remove un-needed code * fix make dev * address comments * Hanfei/join merge (#7) * enable exec details for batch cop * format code * fix test * change tidb_opt_broadcast_join to global vars * Ban cartesian join to be pushed down to TiFlash (#8) * merge master (#10) * merge master * fix bug * fix bug * fix ut * check session var conflict * Add perfer local hint for broadcast join (#12) * update * remove useless code * remove useless code * update parser * add test for prefer local join * use bcj_local * update go.mod * refine planner * refine comments * fix make dev * fix make dev * update parser * address comments * fix make dev * disable broadcast join when new collation is enabled * Update planner/core/exhaust_physical_plans.go Co-authored-by: Zhuomin(Charming) Liu <lzmhhh123@gmail.com> * address comments * fix * address comments * fix tests * address comments Co-authored-by: xufei <xufeixw@mail.ustc.edu.cn> Co-authored-by: xufei <xufei@pingcap.com> Co-authored-by: 虎 <ichneumon.hu@foxmail.com> Co-authored-by: Zhuomin(Charming) Liu <lzmhhh123@gmail.com>
182 lines
6.7 KiB
Go
182 lines
6.7 KiB
Go
// Copyright 2019 PingCAP, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package core
|
|
|
|
import (
|
|
"github.com/pingcap/parser/ast"
|
|
"github.com/pingcap/parser/model"
|
|
"github.com/pingcap/tidb/kv"
|
|
"github.com/pingcap/tidb/sessionctx"
|
|
utilhint "github.com/pingcap/tidb/util/hint"
|
|
)
|
|
|
|
// GenHintsFromPhysicalPlan generates hints from physical plan.
|
|
func GenHintsFromPhysicalPlan(p Plan) []*ast.TableOptimizerHint {
|
|
var hints []*ast.TableOptimizerHint
|
|
switch pp := p.(type) {
|
|
case *Explain:
|
|
return GenHintsFromPhysicalPlan(pp.TargetPlan)
|
|
case *Update:
|
|
hints = genHintsFromPhysicalPlan(pp.SelectPlan, utilhint.TypeUpdate)
|
|
case *Delete:
|
|
hints = genHintsFromPhysicalPlan(pp.SelectPlan, utilhint.TypeDelete)
|
|
case PhysicalPlan:
|
|
hints = genHintsFromPhysicalPlan(pp, utilhint.TypeSelect)
|
|
}
|
|
return hints
|
|
}
|
|
|
|
func getTableName(tblName model.CIStr, asName *model.CIStr) model.CIStr {
|
|
if asName != nil && asName.L != "" {
|
|
return *asName
|
|
}
|
|
return tblName
|
|
}
|
|
|
|
func extractTableAsName(p PhysicalPlan) (*model.CIStr, *model.CIStr) {
|
|
if len(p.Children()) > 1 {
|
|
return nil, nil
|
|
}
|
|
switch x := p.(type) {
|
|
case *PhysicalTableReader:
|
|
ts := x.TablePlans[0].(*PhysicalTableScan)
|
|
if ts.TableAsName.L != "" {
|
|
return &ts.DBName, ts.TableAsName
|
|
}
|
|
return &ts.DBName, &ts.Table.Name
|
|
case *PhysicalIndexReader:
|
|
is := x.IndexPlans[0].(*PhysicalIndexScan)
|
|
if is.TableAsName.L != "" {
|
|
return &is.DBName, is.TableAsName
|
|
}
|
|
return &is.DBName, &is.Table.Name
|
|
case *PhysicalIndexLookUpReader:
|
|
is := x.IndexPlans[0].(*PhysicalIndexScan)
|
|
if is.TableAsName.L != "" {
|
|
return &is.DBName, is.TableAsName
|
|
}
|
|
return &is.DBName, &is.Table.Name
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func getJoinHints(sctx sessionctx.Context, joinType string, parentOffset int, nodeType utilhint.NodeType, children ...PhysicalPlan) (res []*ast.TableOptimizerHint) {
|
|
for _, child := range children {
|
|
blockOffset := child.SelectBlockOffset()
|
|
if blockOffset == -1 {
|
|
continue
|
|
}
|
|
var dbName, tableName *model.CIStr
|
|
if child.SelectBlockOffset() != parentOffset {
|
|
hintTable := sctx.GetSessionVars().PlannerSelectBlockAsName[child.SelectBlockOffset()]
|
|
// For sub-queries like `(select * from t) t1`, t1 should belong to its surrounding select block.
|
|
dbName, tableName, blockOffset = &hintTable.DBName, &hintTable.TableName, parentOffset
|
|
} else {
|
|
dbName, tableName = extractTableAsName(child)
|
|
}
|
|
if tableName == nil {
|
|
continue
|
|
}
|
|
res = append(res, &ast.TableOptimizerHint{
|
|
QBName: utilhint.GenerateQBName(nodeType, blockOffset),
|
|
HintName: model.NewCIStr(joinType),
|
|
Tables: []ast.HintTable{{DBName: *dbName, TableName: *tableName}},
|
|
})
|
|
break
|
|
}
|
|
return res
|
|
}
|
|
|
|
func genHintsFromPhysicalPlan(p PhysicalPlan, nodeType utilhint.NodeType) (res []*ast.TableOptimizerHint) {
|
|
for _, child := range p.Children() {
|
|
res = append(res, genHintsFromPhysicalPlan(child, nodeType)...)
|
|
}
|
|
switch pp := p.(type) {
|
|
case *PhysicalTableReader:
|
|
tbl := pp.TablePlans[0].(*PhysicalTableScan)
|
|
res = append(res, &ast.TableOptimizerHint{
|
|
QBName: utilhint.GenerateQBName(nodeType, pp.blockOffset),
|
|
HintName: model.NewCIStr(HintUseIndex),
|
|
Tables: []ast.HintTable{{DBName: tbl.DBName, TableName: getTableName(tbl.Table.Name, tbl.TableAsName)}},
|
|
})
|
|
if tbl.StoreType == kv.TiFlash {
|
|
res = append(res, &ast.TableOptimizerHint{
|
|
QBName: utilhint.GenerateQBName(nodeType, pp.blockOffset),
|
|
HintName: model.NewCIStr(HintReadFromStorage),
|
|
HintData: model.NewCIStr(kv.TiFlash.Name()),
|
|
Tables: []ast.HintTable{{DBName: tbl.DBName, TableName: getTableName(tbl.Table.Name, tbl.TableAsName)}},
|
|
})
|
|
}
|
|
case *PhysicalIndexLookUpReader:
|
|
index := pp.IndexPlans[0].(*PhysicalIndexScan)
|
|
res = append(res, &ast.TableOptimizerHint{
|
|
QBName: utilhint.GenerateQBName(nodeType, pp.blockOffset),
|
|
HintName: model.NewCIStr(HintUseIndex),
|
|
Tables: []ast.HintTable{{DBName: index.DBName, TableName: getTableName(index.Table.Name, index.TableAsName)}},
|
|
Indexes: []model.CIStr{index.Index.Name},
|
|
})
|
|
case *PhysicalIndexReader:
|
|
index := pp.IndexPlans[0].(*PhysicalIndexScan)
|
|
res = append(res, &ast.TableOptimizerHint{
|
|
QBName: utilhint.GenerateQBName(nodeType, pp.blockOffset),
|
|
HintName: model.NewCIStr(HintUseIndex),
|
|
Tables: []ast.HintTable{{DBName: index.DBName, TableName: getTableName(index.Table.Name, index.TableAsName)}},
|
|
Indexes: []model.CIStr{index.Index.Name},
|
|
})
|
|
case *PhysicalIndexMergeReader:
|
|
Indexs := make([]model.CIStr, 0, 2)
|
|
var tableName model.CIStr
|
|
var tableAsName *model.CIStr
|
|
for _, partialPlan := range pp.PartialPlans {
|
|
if index, ok := partialPlan[0].(*PhysicalIndexScan); ok {
|
|
Indexs = append(Indexs, index.Index.Name)
|
|
tableName = index.Table.Name
|
|
tableAsName = index.TableAsName
|
|
} else {
|
|
indexName := model.NewCIStr("PRIMARY")
|
|
Indexs = append(Indexs, indexName)
|
|
}
|
|
}
|
|
res = append(res, &ast.TableOptimizerHint{
|
|
QBName: utilhint.GenerateQBName(nodeType, pp.blockOffset),
|
|
HintName: model.NewCIStr(HintIndexMerge),
|
|
Tables: []ast.HintTable{{TableName: getTableName(tableName, tableAsName)}},
|
|
Indexes: Indexs,
|
|
})
|
|
case *PhysicalHashAgg:
|
|
res = append(res, &ast.TableOptimizerHint{
|
|
QBName: utilhint.GenerateQBName(nodeType, pp.blockOffset),
|
|
HintName: model.NewCIStr(HintHashAgg),
|
|
})
|
|
case *PhysicalStreamAgg:
|
|
res = append(res, &ast.TableOptimizerHint{
|
|
QBName: utilhint.GenerateQBName(nodeType, pp.blockOffset),
|
|
HintName: model.NewCIStr(HintStreamAgg),
|
|
})
|
|
case *PhysicalMergeJoin:
|
|
res = append(res, getJoinHints(p.SCtx(), HintSMJ, p.SelectBlockOffset(), nodeType, pp.children...)...)
|
|
case *PhysicalBroadCastJoin:
|
|
res = append(res, getJoinHints(p.SCtx(), HintBCJ, p.SelectBlockOffset(), nodeType, pp.children...)...)
|
|
case *PhysicalHashJoin:
|
|
res = append(res, getJoinHints(p.SCtx(), HintHJ, p.SelectBlockOffset(), nodeType, pp.children...)...)
|
|
case *PhysicalIndexJoin:
|
|
res = append(res, getJoinHints(p.SCtx(), HintINLJ, p.SelectBlockOffset(), nodeType, pp.children[pp.InnerChildIdx])...)
|
|
case *PhysicalIndexMergeJoin:
|
|
res = append(res, getJoinHints(p.SCtx(), HintINLMJ, p.SelectBlockOffset(), nodeType, pp.children[pp.InnerChildIdx])...)
|
|
case *PhysicalIndexHashJoin:
|
|
res = append(res, getJoinHints(p.SCtx(), HintINLHJ, p.SelectBlockOffset(), nodeType, pp.children[pp.InnerChildIdx])...)
|
|
}
|
|
return res
|
|
}
|