Skip to content

Commit

Permalink
change some logic
Browse files Browse the repository at this point in the history
  • Loading branch information
winoros committed Mar 20, 2017
1 parent 02cbcf1 commit 9a9ae32
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 56 deletions.
7 changes: 7 additions & 0 deletions plan/logical_plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,13 @@ type Selection struct {
AccessConditions []expression.Expression
IdxConditions []expression.Expression
TblConditions []expression.Expression

// Since one selection may call convert2PhysicalScan many times. We extract the PkName and indices
// used for scanController only once and store them to judge whether this selection can convert to
// scanController mode.
usefulPkName model.CIStr
usefulIndices []*model.IndexInfo
extractedUsefulThing bool
}

func (p *Selection) extractCorrelatedCols() []*expression.CorrelatedColumn {
Expand Down
125 changes: 69 additions & 56 deletions plan/physical_plan_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -891,7 +891,7 @@ func buildIndexScanByKeyAndCorCol(p *DataSource, idx *model.IndexInfo, fakeConds
// The first return value is the new expression, the second is a bool value tell whether the below expression is all constant.
// The Second is used for simplify the scalar function.
// If the args of one scalar function are all constant, we will substitute it to constant.
func substituteCorCol2Constant(cond expression.Expression) (expression.Expression) {
func substituteCorCol2Constant(cond expression.Expression) expression.Expression {
switch x := cond.(type) {
case *expression.ScalarFunction:
newArgs := make([]expression.Expression, 0, len(x.GetArgs()))
Expand Down Expand Up @@ -922,29 +922,35 @@ func substituteCorCol2Constant(cond expression.Expression) (expression.Expressio
}

// getUsableIndicesAndPk will simply check whether the pk or one index could used in this situation by
// checking whether this index or pk is contained in one condition that has correlated column.
// checking whether this index or pk is contained in one condition that has correlated column,
// and whether this condition can be used as an access condition.
func (p *Selection) getUsableIndicesAndPk(ds *DataSource) ([]*model.IndexInfo, model.CIStr) {
indices, _ := availableIndices(ds.indexHints, ds.tableInfo)
var usableIdxs []*model.IndexInfo
for _, idx := range indices {
// Currently we don't consider composite index.
// TODO: Currently we don't consider composite index.
if len(idx.Columns) > 1 {
continue
}
var idxCol *expression.Column
for _, col := range ds.schema.Columns {
if idx.Columns[0].Name.L == col.ColName.L {
idxCol = col
}
checker := &conditionChecker{
idx: idx,
columnOffset: 0,
length: idx.Columns[0].Length,
}
// This idx column should occur in one condition which contains both column and correlated column.
// And conditionChecker.check(this condition) should be true.
var usable bool
for _, cond := range p.Conditions {
cols := expression.ExtractColumns(cond)
if len(cols) != 1 || !cond.IsCorrelated() || !cols[0].Equal(idxCol, p.ctx) {
for _, expr := range p.Conditions {
cond := pushDownNot(expr.Clone(), false, nil)
if !cond.IsCorrelated() {
continue
}
usable = true
newCond := substituteCorCol2Constant(cond)
// If one cond is ok, then this index is useful.
if checker.check(newCond) {
usable = true
break
}
}
if usable {
usableIdxs = append(usableIdxs, idx)
Expand All @@ -958,13 +964,23 @@ func (p *Selection) getUsableIndicesAndPk(ds *DataSource) ([]*model.IndexInfo, m
pkCol = ds.schema.Columns[i]
}
}
// Pk should satisfies the same property.
checker := conditionChecker{
pkName: pkCol.ColName,
length: types.UnspecifiedLength,
}
// Pk should satisfies the same property as the index.
var usable bool
for _, cond := range p.Conditions {
cols := expression.ExtractColumns(cond)
if !cond.IsCorrelated() || !cols[0].Equal(pkCol, p.ctx) {
for _, expr := range p.Conditions {
cond := pushDownNot(expr.Clone(), false, nil)
if !cond.IsCorrelated() {
continue
}
newCond := substituteCorCol2Constant(cond)
// If one cond is ok, then this index is useful.
if checker.check(newCond) {
usable = true
break
}
usable = true
}
if usable {
Expand All @@ -974,49 +990,40 @@ func (p *Selection) getUsableIndicesAndPk(ds *DataSource) ([]*model.IndexInfo, m
return usableIdxs, pkName
}

// tryToBuildIndexScan will check the conditions contain correlated columns whether it can make indexScan.
func (p *Selection) tryToBuildScanByKeyAndCorCol() *physicalPlanInfo {
if ds, ok := p.children[0].(*DataSource); ok && len(p.extractCorrelatedCols()) > 0 {
conds := make([]expression.Expression, 0, len(p.Conditions))
for _, cond := range p.Conditions {
cond = pushDownNot(cond.Clone(), false, nil)
// In this way, we could use the code of refiner.go.
newCond := substituteCorCol2Constant(cond)
conds = append(conds, newCond)
}
indices, pkName := p.getUsableIndicesAndPk(ds)
// buildScanByKeyAndCorCol will build a scan which the condition is controlled by this selection.
func (p *Selection) buildScanByKeyAndCorCol() *physicalPlanInfo {
ds := p.children[0].(*DataSource)
conds := make([]expression.Expression, 0, len(p.Conditions))
for _, expr := range p.Conditions {
cond := pushDownNot(expr.Clone(), false, nil)
// In this way, we could use the code of refiner.go.
newCond := substituteCorCol2Constant(cond)
conds = append(conds, newCond)
}

// If pk is handle, we will try to build TableScan by pk, otherwise we will try to build IndexScan by index.
if pkName.L == "" {
var finalInfo *physicalPlanInfo
for _, idx := range indices {
info, accessConds, idxConds, tblConds := buildIndexScanByKeyAndCorCol(ds, idx, conds, p.Conditions)
if info == nil {
continue
}
if finalInfo == nil || finalInfo.cost > info.cost {
p.ScanController = true
p.AccessConditions = accessConds
p.IdxConditions = idxConds
p.TblConditions = tblConds
finalInfo = info
}
// If pk is handle, we will try to build TableScan by pk, otherwise we will try to build IndexScan by index.
if p.usefulPkName.L == "" {
var finalInfo *physicalPlanInfo
for _, idx := range p.usefulIndices {
info, accessConds, idxConds, tblConds := buildIndexScanByKeyAndCorCol(ds, idx, conds, p.Conditions)
if info == nil {
continue
}
if finalInfo == nil {
return nil
if finalInfo == nil || finalInfo.cost > info.cost {
p.ScanController = true
p.AccessConditions = accessConds
p.IdxConditions = idxConds
p.TblConditions = tblConds
finalInfo = info
}
return p.appendSelToInfo(finalInfo)
}
info, accessCondition, filterCondition := buildTableScanByKeyAndCorCol(ds, pkName, conds, p.Conditions)
if info == nil {
return nil
}
p.ScanController = true
p.AccessConditions = accessCondition
p.TblConditions = filterCondition
return p.appendSelToInfo(info)
return p.appendSelToInfo(finalInfo)
}
return nil
info, accessCondition, filterCondition := buildTableScanByKeyAndCorCol(ds, p.usefulPkName, conds, p.Conditions)
p.ScanController = true
p.AccessConditions = accessCondition
p.TblConditions = filterCondition
return p.appendSelToInfo(info)
}

// convert2PhysicalPlan implements the LogicalPlan convert2PhysicalPlan interface.
Expand All @@ -1028,8 +1035,14 @@ func (p *Selection) convert2PhysicalPlan(prop *requiredProperty) (*physicalPlanI
if info != nil {
return info, nil
}
info = p.tryToBuildScanByKeyAndCorCol()
if info != nil {
if !p.extractedUsefulThing {
if ds, ok := p.children[0].(*DataSource); ok {
p.getUsableIndicesAndPk(ds)
}
p.extractedUsefulThing = true
}
if p.usefulPkName.L != "" || len(p.usefulIndices) > 0 {
info = p.buildScanByKeyAndCorCol()
p.storePlanInfo(prop, info)
return info, nil
}
Expand Down

0 comments on commit 9a9ae32

Please sign in to comment.