planner: classify logical index scan into a separate file for later pkg move (#54443)

ref pingcap/tidb#51664, ref pingcap/tidb#52714
This commit is contained in:
Arenatlx
2024-07-05 10:38:58 +08:00
committed by GitHub
parent a220260518
commit 99799fea4d
10 changed files with 216 additions and 136 deletions

View File

@ -220,7 +220,7 @@ func (*ImplIndexScan) Match(expr *memo.GroupExpr, prop *property.PhysicalPropert
// OnImplement implements ImplementationRule OnImplement interface.
func (*ImplIndexScan) OnImplement(expr *memo.GroupExpr, reqProp *property.PhysicalProperty) ([]memo.Implementation, error) {
logicalScan := expr.ExprNode.(*plannercore.LogicalIndexScan)
is := logicalScan.GetPhysicalIndexScan(expr.Group.Prop.Schema, expr.Group.Prop.Stats.ScaleByExpectCnt(reqProp.ExpectedCnt))
is := plannercore.GetPhysicalIndexScan4LogicalIndexScan(logicalScan, expr.Group.Prop.Schema, expr.Group.Prop.Stats.ScaleByExpectCnt(reqProp.ExpectedCnt))
if !reqProp.IsSortItemEmpty() {
is.KeepOrder = true
if reqProp.SortItems[0].Desc {

View File

@ -22,6 +22,7 @@ go_library(
"indexmerge_unfinished_path.go",
"initialize.go",
"logical_aggregation.go",
"logical_index_scan.go",
"logical_initialize.go",
"logical_limit.go",
"logical_lock.go",

View File

@ -1006,29 +1006,6 @@ func explainNormalizedByItems(buffer *bytes.Buffer, byItems []*util.ByItems) *by
return buffer
}
// ExplainInfo implements Plan interface.
func (p *LogicalIndexScan) ExplainInfo() string {
buffer := bytes.NewBufferString(p.Source.ExplainInfo())
index := p.Index
if len(index.Columns) > 0 {
buffer.WriteString(", index:")
for i, idxCol := range index.Columns {
if tblCol := p.Source.TableInfo.Columns[idxCol.Offset]; tblCol.Hidden {
buffer.WriteString(tblCol.GeneratedExprString)
} else {
buffer.WriteString(idxCol.Name.O)
}
if i+1 < len(index.Columns) {
buffer.WriteString(", ")
}
}
}
if len(p.AccessConds) > 0 {
fmt.Fprintf(buffer, ", cond:%v", p.AccessConds)
}
return buffer.String()
}
// ExplainInfo implements Plan interface.
func (p *PhysicalMemTable) ExplainInfo() string {
accessObject, operatorInfo := p.AccessObject().String(), p.OperatorInfo(false)

View File

@ -2369,8 +2369,8 @@ func GetPhysicalScan4LogicalTableScan(s *LogicalTableScan, schema *expression.Sc
return ts
}
// GetPhysicalIndexScan returns PhysicalIndexScan for the logical IndexScan.
func (s *LogicalIndexScan) GetPhysicalIndexScan(_ *expression.Schema, stats *property.StatsInfo) *PhysicalIndexScan {
// GetPhysicalIndexScan4LogicalIndexScan returns PhysicalIndexScan for the logical IndexScan.
func GetPhysicalIndexScan4LogicalIndexScan(s *LogicalIndexScan, _ *expression.Schema, stats *property.StatsInfo) *PhysicalIndexScan {
ds := s.Source
is := PhysicalIndexScan{
Table: ds.TableInfo,

View File

@ -0,0 +1,212 @@
// Copyright 2024 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,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package core
import (
"bytes"
"fmt"
"github.com/pingcap/tidb/pkg/expression"
"github.com/pingcap/tidb/pkg/parser/model"
"github.com/pingcap/tidb/pkg/parser/mysql"
"github.com/pingcap/tidb/pkg/planner/core/base"
"github.com/pingcap/tidb/pkg/planner/core/operator/logicalop"
"github.com/pingcap/tidb/pkg/planner/property"
"github.com/pingcap/tidb/pkg/types"
"github.com/pingcap/tidb/pkg/util/plancodec"
"github.com/pingcap/tidb/pkg/util/ranger"
)
// LogicalIndexScan is the logical index scan operator for TiKV.
type LogicalIndexScan struct {
logicalop.LogicalSchemaProducer
// DataSource should be read-only here.
Source *DataSource
IsDoubleRead bool
EqCondCount int
AccessConds expression.CNFExprs
Ranges []*ranger.Range
Index *model.IndexInfo
Columns []*model.ColumnInfo
FullIdxCols []*expression.Column
FullIdxColLens []int
IdxCols []*expression.Column
IdxColLens []int
}
// Init initializes LogicalIndexScan.
func (is LogicalIndexScan) Init(ctx base.PlanContext, offset int) *LogicalIndexScan {
is.BaseLogicalPlan = logicalop.NewBaseLogicalPlan(ctx, plancodec.TypeIdxScan, &is, offset)
return &is
}
// *************************** start implementation of Plan interface ***************************
// ExplainInfo implements Plan interface.
func (is *LogicalIndexScan) ExplainInfo() string {
buffer := bytes.NewBufferString(is.Source.ExplainInfo())
index := is.Index
if len(index.Columns) > 0 {
buffer.WriteString(", index:")
for i, idxCol := range index.Columns {
if tblCol := is.Source.TableInfo.Columns[idxCol.Offset]; tblCol.Hidden {
buffer.WriteString(tblCol.GeneratedExprString)
} else {
buffer.WriteString(idxCol.Name.O)
}
if i+1 < len(index.Columns) {
buffer.WriteString(", ")
}
}
}
if len(is.AccessConds) > 0 {
fmt.Fprintf(buffer, ", cond:%v", is.AccessConds)
}
return buffer.String()
}
// *************************** end implementation of Plan interface ***************************
// *************************** start implementation of logicalPlan interface ***************************
// HashCode inherits BaseLogicalPlan.<0th> interface.
// PredicatePushDown inherits BaseLogicalPlan.<1st> interface.
// PruneColumns inherits BaseLogicalPlan.<2nd> interface.
// FindBestTask inherits BaseLogicalPlan.<3rd> interface.
// BuildKeyInfo implements base.LogicalPlan.<4th> interface.
func (is *LogicalIndexScan) BuildKeyInfo(selfSchema *expression.Schema, _ []*expression.Schema) {
selfSchema.Keys = nil
for _, path := range is.Source.PossibleAccessPaths {
if path.IsTablePath() {
continue
}
if uniqueKey, newKey := checkIndexCanBeKey(path.Index, is.Columns, selfSchema); newKey != nil {
selfSchema.Keys = append(selfSchema.Keys, newKey)
} else if uniqueKey != nil {
selfSchema.UniqueKeys = append(selfSchema.UniqueKeys, uniqueKey)
}
}
handle := is.getPKIsHandleCol(selfSchema)
if handle != nil {
selfSchema.Keys = append(selfSchema.Keys, []*expression.Column{handle})
}
}
// PushDownTopN inherits BaseLogicalPlan.<5th> interface.
// DeriveTopN inherits BaseLogicalPlan.LogicalPlan.<6th> implementation.
// PredicateSimplification inherits BaseLogicalPlan.LogicalPlan.<7th> implementation.
// ConstantPropagation inherits BaseLogicalPlan.LogicalPlan.<8th> implementation.
// PullUpConstantPredicates inherits BaseLogicalPlan.LogicalPlan.<9th> implementation.
// RecursiveDeriveStats inherits BaseLogicalPlan.LogicalPlan.<10th> implementation.
// DeriveStats implements base.LogicalPlan.<11th> interface.
func (is *LogicalIndexScan) DeriveStats(_ []*property.StatsInfo, selfSchema *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) {
is.Source.initStats(nil)
exprCtx := is.SCtx().GetExprCtx()
for i, expr := range is.AccessConds {
is.AccessConds[i] = expression.PushDownNot(exprCtx, expr)
}
is.SetStats(is.Source.deriveStatsByFilter(is.AccessConds, nil))
if len(is.AccessConds) == 0 {
is.Ranges = ranger.FullRange()
}
is.IdxCols, is.IdxColLens = expression.IndexInfo2PrefixCols(is.Columns, selfSchema.Columns, is.Index)
is.FullIdxCols, is.FullIdxColLens = expression.IndexInfo2Cols(is.Columns, selfSchema.Columns, is.Index)
if !is.Index.Unique && !is.Index.Primary && len(is.Index.Columns) == len(is.IdxCols) {
handleCol := is.getPKIsHandleCol(selfSchema)
if handleCol != nil && !mysql.HasUnsignedFlag(handleCol.RetType.GetFlag()) {
is.IdxCols = append(is.IdxCols, handleCol)
is.IdxColLens = append(is.IdxColLens, types.UnspecifiedLength)
}
}
return is.StatsInfo(), nil
}
// ExtractColGroups inherits BaseLogicalPlan.LogicalPlan.<12th> implementation.
// PreparePossibleProperties implements base.LogicalPlan.<13th> interface.
func (is *LogicalIndexScan) PreparePossibleProperties(_ *expression.Schema, _ ...[][]*expression.Column) [][]*expression.Column {
if len(is.IdxCols) == 0 {
return nil
}
result := make([][]*expression.Column, 0, is.EqCondCount+1)
for i := 0; i <= is.EqCondCount; i++ {
result = append(result, make([]*expression.Column, len(is.IdxCols)-i))
copy(result[i], is.IdxCols[i:])
}
return result
}
// ExhaustPhysicalPlans inherits BaseLogicalPlan.LogicalPlan.<14th> implementation.
// ExtractCorrelatedCols inherits BaseLogicalPlan.LogicalPlan.<15th> implementation.
// MaxOneRow inherits BaseLogicalPlan.LogicalPlan.<16th> implementation.
// Children inherits BaseLogicalPlan.LogicalPlan.<17th> implementation.
// SetChildren inherits BaseLogicalPlan.LogicalPlan.<18th> implementation.
// SetChild inherits BaseLogicalPlan.LogicalPlan.<19th> implementation.
// RollBackTaskMap inherits BaseLogicalPlan.LogicalPlan.<20th> implementation.
// CanPushToCop inherits BaseLogicalPlan.LogicalPlan.<21st> implementation.
// ExtractFD inherits BaseLogicalPlan.LogicalPlan.<22nd> implementation.
// GetBaseLogicalPlan inherits BaseLogicalPlan.LogicalPlan.<23rd> implementation.
// ConvertOuterToInnerJoin inherits BaseLogicalPlan.LogicalPlan.<24th> implementation.
// *************************** end implementation of logicalPlan interface ***************************
// MatchIndexProp checks if the indexScan can match the required property.
func (is *LogicalIndexScan) MatchIndexProp(prop *property.PhysicalProperty) (match bool) {
if prop.IsSortItemEmpty() {
return true
}
if all, _ := prop.AllSameOrder(); !all {
return false
}
sctx := is.SCtx()
evalCtx := sctx.GetExprCtx().GetEvalCtx()
for i, col := range is.IdxCols {
if col.Equal(evalCtx, prop.SortItems[0].Col) {
return matchIndicesProp(sctx, is.IdxCols[i:], is.IdxColLens[i:], prop.SortItems)
} else if i >= is.EqCondCount {
break
}
}
return false
}
func (is *LogicalIndexScan) getPKIsHandleCol(schema *expression.Schema) *expression.Column {
// We cannot use p.Source.getPKIsHandleCol() here,
// Because we may re-prune p.Columns and p.schema during the transformation.
// That will make p.Columns different from p.Source.Columns.
return getPKIsHandleColFromSchema(is.Columns, schema, is.Source.TableInfo.PKIsHandle)
}

View File

@ -32,12 +32,6 @@ func (ds DataSource) Init(ctx base.PlanContext, offset int) *DataSource {
return &ds
}
// Init initializes LogicalIndexScan.
func (is LogicalIndexScan) Init(ctx base.PlanContext, offset int) *LogicalIndexScan {
is.BaseLogicalPlan = logicalop.NewBaseLogicalPlan(ctx, plancodec.TypeIdxScan, &is, offset)
return &is
}
// Init initializes LogicalApply.
func (la LogicalApply) Init(ctx base.PlanContext, offset int) *LogicalApply {
la.BaseLogicalPlan = logicalop.NewBaseLogicalPlan(ctx, plancodec.TypeApply, &la, offset)

View File

@ -1046,45 +1046,6 @@ func (ds *DataSource) ExtractCorrelatedCols() []*expression.CorrelatedColumn {
return corCols
}
// LogicalIndexScan is the logical index scan operator for TiKV.
type LogicalIndexScan struct {
logicalop.LogicalSchemaProducer
// DataSource should be read-only here.
Source *DataSource
IsDoubleRead bool
EqCondCount int
AccessConds expression.CNFExprs
Ranges []*ranger.Range
Index *model.IndexInfo
Columns []*model.ColumnInfo
FullIdxCols []*expression.Column
FullIdxColLens []int
IdxCols []*expression.Column
IdxColLens []int
}
// MatchIndexProp checks if the indexScan can match the required property.
func (p *LogicalIndexScan) MatchIndexProp(prop *property.PhysicalProperty) (match bool) {
if prop.IsSortItemEmpty() {
return true
}
if all, _ := prop.AllSameOrder(); !all {
return false
}
sctx := p.SCtx()
evalCtx := sctx.GetExprCtx().GetEvalCtx()
for i, col := range p.IdxCols {
if col.Equal(evalCtx, prop.SortItems[0].Col) {
return matchIndicesProp(sctx, p.IdxCols[i:], p.IdxColLens[i:], prop.SortItems)
} else if i >= p.EqCondCount {
break
}
}
return false
}
// getTablePath finds the TablePath from a group of accessPaths.
func getTablePath(paths []*util.AccessPath) *util.AccessPath {
for _, path := range paths {
@ -1408,13 +1369,6 @@ func (ds *DataSource) getPKIsHandleCol() *expression.Column {
return getPKIsHandleColFromSchema(ds.Columns, ds.Schema(), ds.TableInfo.PKIsHandle)
}
func (p *LogicalIndexScan) getPKIsHandleCol(schema *expression.Schema) *expression.Column {
// We cannot use p.Source.getPKIsHandleCol() here,
// Because we may re-prune p.Columns and p.schema during the transformation.
// That will make p.Columns different from p.Source.Columns.
return getPKIsHandleColFromSchema(p.Columns, schema, p.Source.TableInfo.PKIsHandle)
}
// WindowFrame represents a window function frame.
type WindowFrame struct {
Type ast.FrameType

View File

@ -56,19 +56,6 @@ func (ds *DataSource) PreparePossibleProperties(_ *expression.Schema, _ ...[][]*
return result
}
// PreparePossibleProperties implements base.LogicalPlan PreparePossibleProperties interface.
func (is *LogicalIndexScan) PreparePossibleProperties(_ *expression.Schema, _ ...[][]*expression.Column) [][]*expression.Column {
if len(is.IdxCols) == 0 {
return nil
}
result := make([][]*expression.Column, 0, is.EqCondCount+1)
for i := 0; i <= is.EqCondCount; i++ {
result = append(result, make([]*expression.Column, len(is.IdxCols)-i))
copy(result[i], is.IdxCols[i:])
}
return result
}
func getPossiblePropertyFromByItems(items []*util.ByItems) []*expression.Column {
cols := make([]*expression.Column, 0, len(items))
for _, item := range items {

View File

@ -197,25 +197,6 @@ func (ds *DataSource) BuildKeyInfo(selfSchema *expression.Schema, _ []*expressio
}
}
// BuildKeyInfo implements base.LogicalPlan BuildKeyInfo interface.
func (is *LogicalIndexScan) BuildKeyInfo(selfSchema *expression.Schema, _ []*expression.Schema) {
selfSchema.Keys = nil
for _, path := range is.Source.PossibleAccessPaths {
if path.IsTablePath() {
continue
}
if uniqueKey, newKey := checkIndexCanBeKey(path.Index, is.Columns, selfSchema); newKey != nil {
selfSchema.Keys = append(selfSchema.Keys, newKey)
} else if uniqueKey != nil {
selfSchema.UniqueKeys = append(selfSchema.UniqueKeys, uniqueKey)
}
}
handle := is.getPKIsHandleCol(selfSchema)
if handle != nil {
selfSchema.Keys = append(selfSchema.Keys, []*expression.Column{handle})
}
}
func (*buildKeySolver) name() string {
return "build_keys"
}

View File

@ -28,7 +28,6 @@ import (
"github.com/pingcap/tidb/pkg/infoschema"
"github.com/pingcap/tidb/pkg/kv"
"github.com/pingcap/tidb/pkg/parser/model"
"github.com/pingcap/tidb/pkg/parser/mysql"
"github.com/pingcap/tidb/pkg/planner/cardinality"
"github.com/pingcap/tidb/pkg/planner/core/base"
"github.com/pingcap/tidb/pkg/planner/core/cost"
@ -40,10 +39,8 @@ import (
"github.com/pingcap/tidb/pkg/statistics"
"github.com/pingcap/tidb/pkg/statistics/asyncload"
"github.com/pingcap/tidb/pkg/table"
"github.com/pingcap/tidb/pkg/types"
h "github.com/pingcap/tidb/pkg/util/hint"
"github.com/pingcap/tidb/pkg/util/logutil"
"github.com/pingcap/tidb/pkg/util/ranger"
"go.uber.org/zap"
)
@ -475,29 +472,6 @@ func getMinSelectivityFromPaths(paths []*util.AccessPath, totalRowCount float64)
return minSelectivity
}
// DeriveStats implements LogicalPlan DeriveStats interface.
func (is *LogicalIndexScan) DeriveStats(_ []*property.StatsInfo, selfSchema *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) {
is.Source.initStats(nil)
exprCtx := is.SCtx().GetExprCtx()
for i, expr := range is.AccessConds {
is.AccessConds[i] = expression.PushDownNot(exprCtx, expr)
}
is.SetStats(is.Source.deriveStatsByFilter(is.AccessConds, nil))
if len(is.AccessConds) == 0 {
is.Ranges = ranger.FullRange()
}
is.IdxCols, is.IdxColLens = expression.IndexInfo2PrefixCols(is.Columns, selfSchema.Columns, is.Index)
is.FullIdxCols, is.FullIdxColLens = expression.IndexInfo2Cols(is.Columns, selfSchema.Columns, is.Index)
if !is.Index.Unique && !is.Index.Primary && len(is.Index.Columns) == len(is.IdxCols) {
handleCol := is.getPKIsHandleCol(selfSchema)
if handleCol != nil && !mysql.HasUnsignedFlag(handleCol.RetType.GetFlag()) {
is.IdxCols = append(is.IdxCols, handleCol)
is.IdxColLens = append(is.IdxColLens, types.UnspecifiedLength)
}
}
return is.StatsInfo(), nil
}
func deriveLimitStats(childProfile *property.StatsInfo, limitCount float64) *property.StatsInfo {
stats := &property.StatsInfo{
RowCount: math.Min(limitCount, childProfile.RowCount),