Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

planner: refactor some code related to IndexMerge and MVIndex #40800

Merged
merged 8 commits into from
Jan 29, 2023
105 changes: 55 additions & 50 deletions planner/core/indexmerge_path.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
package core

import (
"fmt"
"math"
"strings"

Expand All @@ -37,6 +36,16 @@ import (

// generateIndexMergePath generates IndexMerge AccessPaths on this DataSource.
func (ds *DataSource) generateIndexMergePath() error {
var warningMsg string
stmtCtx := ds.ctx.GetSessionVars().StmtCtx
defer func() {
if len(ds.indexMergeHints) > 0 && warningMsg != "" {
ds.indexMergeHints = nil
stmtCtx.AppendWarning(errors.Errorf(warningMsg))
logutil.BgLogger().Debug(warningMsg)
}
}()

// Consider the IndexMergePath. Now, we just generate `IndexMergePath` in DNF case.
// Use allConds instread of pushedDownConds,
// because we want to use IndexMerge even if some expr cannot be pushed to TiKV.
Expand All @@ -46,11 +55,26 @@ func (ds *DataSource) generateIndexMergePath() error {
indexMergeConds = append(indexMergeConds, expression.PushDownNot(ds.ctx, expr))
}

stmtCtx := ds.ctx.GetSessionVars().StmtCtx
isPossibleIdxMerge := len(indexMergeConds) > 0 && // have corresponding access conditions, and
(len(ds.possibleAccessPaths) > 1 || // (have multiple index paths, or
(len(ds.possibleAccessPaths) == 1 && isMVIndexPath(ds.possibleAccessPaths[0]))) // have a MVIndex)

if !isPossibleIdxMerge {
warningMsg = "IndexMerge is inapplicable or disabled. No available filter or available index."
return nil
}

sessionAndStmtPermission := (ds.ctx.GetSessionVars().GetEnableIndexMerge() || len(ds.indexMergeHints) > 0) && !stmtCtx.NoIndexMergeHint
if !sessionAndStmtPermission {
warningMsg = "IndexMerge is inapplicable or disabled. Got no_index_merge hint or tidb_enable_index_merge is off."
return nil
}

if ds.tableInfo.TempTableType == model.TempTableLocal {
warningMsg = "IndexMerge is inapplicable or disabled. Cannot use IndexMerge on temporary table."
return nil
}

// We current do not consider `IndexMergePath`:
// 1. If there is an index path.
// 2. TODO: If there exists exprs that cannot be pushed down. This is to avoid wrongly estRow of Selection added by rule_predicate_push_down.
Expand Down Expand Up @@ -87,26 +111,37 @@ func (ds *DataSource) generateIndexMergePath() error {
}
}

if isPossibleIdxMerge && sessionAndStmtPermission && needConsiderIndexMerge && ds.tableInfo.TempTableType != model.TempTableLocal {
err := ds.generateAndPruneIndexMergePath(indexMergeConds, ds.indexMergeHints != nil)
if err != nil {
return err
}
} else if len(ds.indexMergeHints) > 0 {
if !needConsiderIndexMerge {
warningMsg = "IndexMerge is inapplicable or disabled. "
return nil
}
regularPathCount := len(ds.possibleAccessPaths)
if err := ds.generateAndPruneIndexMergePath(indexMergeConds); err != nil {
return err
}

// If without hints, it means that `enableIndexMerge` is true
if len(ds.indexMergeHints) == 0 {
return nil
}
// If len(indexMergeHints) > 0, then add warnings if index-merge hints cannot work.
if regularPathCount == len(ds.possibleAccessPaths) {
ds.indexMergeHints = nil
var msg string
if !isPossibleIdxMerge {
msg = "No available filter or available index."
} else if !sessionAndStmtPermission {
msg = "Got no_index_merge hint or tidb_enable_index_merge is off."
} else if ds.tableInfo.TempTableType == model.TempTableLocal {
msg = "Cannot use IndexMerge on temporary table."
}
msg = fmt.Sprintf("IndexMerge is inapplicable or disabled. %s", msg)
stmtCtx.AppendWarning(errors.Errorf(msg))
logutil.BgLogger().Debug(msg)
ds.ctx.GetSessionVars().StmtCtx.AppendWarning(errors.Errorf("IndexMerge is inapplicable"))
return nil
}

// If len(indexMergeHints) > 0 and some index-merge paths were added, then prune all other non-index-merge paths.
ds.possibleAccessPaths = ds.possibleAccessPaths[regularPathCount:]
minRowCount := ds.possibleAccessPaths[0].CountAfterAccess
for _, path := range ds.possibleAccessPaths {
if minRowCount < path.CountAfterAccess {
minRowCount = path.CountAfterAccess
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that minRowCount is the maximum of each path's CountAfterAccess, which is confusing. Since the PR is a refactor, we can solve the problem in the future PR.

}
}
if ds.stats.RowCount > minRowCount {
ds.stats = ds.tableStats.ScaleByExpectCnt(minRowCount)
}
return nil
}

Expand Down Expand Up @@ -441,7 +476,7 @@ func (ds *DataSource) generateIndexMergeAndPaths(normalPathCnt int) *util.Access
return indexMergePath
}

func (ds *DataSource) generateAndPruneIndexMergePath(indexMergeConds []expression.Expression, needPrune bool) error {
func (ds *DataSource) generateAndPruneIndexMergePath(indexMergeConds []expression.Expression) error {
regularPathCount := len(ds.possibleAccessPaths)
// 1. Generate possible IndexMerge paths for `OR`.
err := ds.generateIndexMergeOrPaths(indexMergeConds)
Expand All @@ -461,36 +496,6 @@ func (ds *DataSource) generateAndPruneIndexMergePath(indexMergeConds []expressio
if mvIndexMergePath != nil {
ds.possibleAccessPaths = append(ds.possibleAccessPaths, mvIndexMergePath...)
}

// 4. If needed, append a warning if no IndexMerge is generated.

// If without hints, it means that `enableIndexMerge` is true
if len(ds.indexMergeHints) == 0 {
return nil
}
// With hints and without generated IndexMerge paths
if regularPathCount == len(ds.possibleAccessPaths) {
ds.indexMergeHints = nil
ds.ctx.GetSessionVars().StmtCtx.AppendWarning(errors.Errorf("IndexMerge is inapplicable"))
return nil
}

// 4. If needPrune is true, prune non-IndexMerge paths.

// Do not need to consider the regular paths in find_best_task().
// So we can use index merge's row count as DataSource's row count.
if needPrune {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can remove the argument needPrune now.

Copy link
Contributor Author

@qw4990 qw4990 Jan 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right. It was removed now.

ds.possibleAccessPaths = ds.possibleAccessPaths[regularPathCount:]
minRowCount := ds.possibleAccessPaths[0].CountAfterAccess
for _, path := range ds.possibleAccessPaths {
if minRowCount < path.CountAfterAccess {
minRowCount = path.CountAfterAccess
}
}
if ds.stats.RowCount > minRowCount {
ds.stats = ds.tableStats.ScaleByExpectCnt(minRowCount)
}
}
return nil
}

Expand Down