91 Star 506 Fork 154

平凯星辰(北京)科技有限公司/tidb

加入 Gitee
与超过 1400万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
gen_physical_plans.go 22.83 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
// Copyright 2017 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 plan
import (
"math"
"github.com/juju/errors"
"github.com/pingcap/tidb/ast"
"github.com/pingcap/tidb/expression"
"github.com/pingcap/tidb/expression/aggregation"
"github.com/pingcap/tidb/model"
"github.com/pingcap/tidb/mysql"
"github.com/pingcap/tidb/terror"
"github.com/pingcap/tidb/types"
"github.com/pingcap/tidb/util/ranger"
)
func (p *LogicalUnionScan) exhaustPhysicalPlans(prop *requiredProp) []PhysicalPlan {
us := PhysicalUnionScan{Conditions: p.conditions}.init(p.ctx, p.stats, prop)
return []PhysicalPlan{us}
}
func getPermutation(cols1, cols2 []*expression.Column) ([]int, []*expression.Column) {
tmpSchema := expression.NewSchema(cols2...)
permutation := make([]int, 0, len(cols1))
for i, col1 := range cols1 {
offset := tmpSchema.ColumnIndex(col1)
if offset == -1 {
return permutation, cols1[:i]
}
permutation = append(permutation, offset)
}
return permutation, cols1
}
func findMaxPrefixLen(candidates [][]*expression.Column, keys []*expression.Column) int {
maxLen := 0
for _, candidateKeys := range candidates {
matchedLen := 0
for i := range keys {
if i < len(candidateKeys) && keys[i].Equal(nil, candidateKeys[i]) {
matchedLen++
} else {
break
}
}
if matchedLen > maxLen {
maxLen = matchedLen
}
}
return maxLen
}
func (p *LogicalJoin) moveEqualToOtherConditions(offsets []int) []expression.Expression {
otherConds := make([]expression.Expression, len(p.OtherConditions))
copy(otherConds, p.OtherConditions)
for i, eqCond := range p.EqualConditions {
match := false
for _, offset := range offsets {
if i == offset {
match = true
break
}
}
if !match {
otherConds = append(otherConds, eqCond)
}
}
return otherConds
}
// Only if the input required prop is the prefix fo join keys, we can pass through this property.
func (p *PhysicalMergeJoin) tryToGetChildReqProp(prop *requiredProp) ([]*requiredProp, bool) {
lProp := &requiredProp{taskTp: rootTaskType, cols: p.LeftKeys, expectedCnt: math.MaxFloat64}
rProp := &requiredProp{taskTp: rootTaskType, cols: p.RightKeys, expectedCnt: math.MaxFloat64}
if !prop.isEmpty() {
// sort merge join fits the cases of massive ordered data, so desc scan is always expensive.
if prop.desc {
return nil, false
}
if !prop.isPrefix(lProp) && !prop.isPrefix(rProp) {
return nil, false
}
if prop.isPrefix(rProp) && p.JoinType == LeftOuterJoin {
return nil, false
}
if prop.isPrefix(lProp) && p.JoinType == RightOuterJoin {
return nil, false
}
}
return []*requiredProp{lProp, rProp}, true
}
func (p *LogicalJoin) getMergeJoin(prop *requiredProp) []PhysicalPlan {
joins := make([]PhysicalPlan, 0, len(p.leftProperties))
// The leftProperties caches all the possible properties that are provided by its children.
for _, leftCols := range p.leftProperties {
offsets, leftKeys := getPermutation(leftCols, p.LeftJoinKeys)
if len(offsets) == 0 {
continue
}
rightKeys := expression.NewSchema(p.RightJoinKeys...).ColumnsByIndices(offsets)
prefixLen := findMaxPrefixLen(p.rightProperties, rightKeys)
if prefixLen == 0 {
continue
}
leftKeys = leftKeys[:prefixLen]
rightKeys = rightKeys[:prefixLen]
offsets = offsets[:prefixLen]
mergeJoin := PhysicalMergeJoin{
JoinType: p.JoinType,
LeftConditions: p.LeftConditions,
RightConditions: p.RightConditions,
DefaultValues: p.DefaultValues,
LeftKeys: leftKeys,
RightKeys: rightKeys,
}.init(p.ctx, p.stats.scaleByExpectCnt(prop.expectedCnt))
mergeJoin.SetSchema(p.schema)
mergeJoin.OtherConditions = p.moveEqualToOtherConditions(offsets)
if reqProps, ok := mergeJoin.tryToGetChildReqProp(prop); ok {
mergeJoin.childrenReqProps = reqProps
joins = append(joins, mergeJoin)
}
}
return joins
}
func (p *LogicalJoin) getHashJoins(prop *requiredProp) []PhysicalPlan {
if !prop.isEmpty() { // hash join doesn't promise any orders
return nil
}
joins := make([]PhysicalPlan, 0, 2)
switch p.JoinType {
case SemiJoin, AntiSemiJoin, LeftOuterSemiJoin, AntiLeftOuterSemiJoin, LeftOuterJoin:
joins = append(joins, p.getHashJoin(prop, 1))
case RightOuterJoin:
joins = append(joins, p.getHashJoin(prop, 0))
case InnerJoin:
joins = append(joins, p.getHashJoin(prop, 1))
joins = append(joins, p.getHashJoin(prop, 0))
}
return joins
}
func (p *LogicalJoin) getHashJoin(prop *requiredProp, innerIdx int) *PhysicalHashJoin {
chReqProps := make([]*requiredProp, 2)
chReqProps[innerIdx] = &requiredProp{expectedCnt: math.MaxFloat64}
chReqProps[1-innerIdx] = &requiredProp{expectedCnt: prop.expectedCnt}
hashJoin := PhysicalHashJoin{
EqualConditions: p.EqualConditions,
LeftConditions: p.LeftConditions,
RightConditions: p.RightConditions,
OtherConditions: p.OtherConditions,
JoinType: p.JoinType,
Concurrency: uint(p.ctx.GetSessionVars().HashJoinConcurrency),
DefaultValues: p.DefaultValues,
InnerChildIdx: innerIdx,
}.init(p.ctx, p.stats.scaleByExpectCnt(prop.expectedCnt), chReqProps...)
hashJoin.SetSchema(p.schema)
return hashJoin
}
// joinKeysMatchIndex checks if all keys match columns in index.
// It returns a slice a[] what a[i] means keys[i] is related with indexCols[a[i]].
func joinKeysMatchIndex(keys, indexCols []*expression.Column, colLengths []int) []int {
if len(indexCols) < len(keys) {
return nil
}
keyOff2IdxOff := make([]int, len(keys))
for keyOff, key := range keys {
idxOff := joinKeyMatchIndexCol(key, indexCols, colLengths)
if idxOff == -1 {
return nil
}
keyOff2IdxOff[keyOff] = idxOff
}
return keyOff2IdxOff
}
func joinKeyMatchIndexCol(key *expression.Column, indexCols []*expression.Column, colLengths []int) int {
for idxOff, idxCol := range indexCols {
if colLengths[idxOff] != types.UnspecifiedLength {
continue
}
if idxCol.ColName.L == key.ColName.L {
return idxOff
}
}
return -1
}
// When inner plan is TableReader, the last two parameter will be nil
func (p *LogicalJoin) constructIndexJoin(prop *requiredProp, innerJoinKeys, outerJoinKeys []*expression.Column, outerIdx int,
innerPlan PhysicalPlan, ranges []*ranger.NewRange, keyOff2IdxOff []int) []PhysicalPlan {
joinType := p.JoinType
outerSchema := p.children[outerIdx].Schema()
// If the order by columns are not all from outer child, index join cannot promise the order.
if !prop.allColsFromSchema(outerSchema) {
return nil
}
chReqProps := make([]*requiredProp, 2)
chReqProps[outerIdx] = &requiredProp{taskTp: rootTaskType, expectedCnt: prop.expectedCnt, cols: prop.cols, desc: prop.desc}
join := PhysicalIndexJoin{
OuterIndex: outerIdx,
LeftConditions: p.LeftConditions,
RightConditions: p.RightConditions,
OtherConditions: p.OtherConditions,
JoinType: joinType,
OuterJoinKeys: outerJoinKeys,
InnerJoinKeys: innerJoinKeys,
DefaultValues: p.DefaultValues,
innerPlan: innerPlan,
KeyOff2IdxOff: keyOff2IdxOff,
Ranges: ranges,
}.init(p.ctx, p.stats.scaleByExpectCnt(prop.expectedCnt), chReqProps...)
join.SetSchema(p.schema)
return []PhysicalPlan{join}
}
// getIndexJoinByOuterIdx will generate index join by outerIndex. OuterIdx points out the outer child.
// First of all, we'll check whether the inner child is DataSource.
// Then, we will extract the join keys of p's equal conditions. Then check whether all of them are just the primary key
// or match some part of on index. If so we will choose the best one and construct a index join.
func (p *LogicalJoin) getIndexJoinByOuterIdx(prop *requiredProp, outerIdx int) []PhysicalPlan {
innerChild := p.children[1-outerIdx]
var (
innerJoinKeys []*expression.Column
outerJoinKeys []*expression.Column
)
if outerIdx == 0 {
outerJoinKeys = p.LeftJoinKeys
innerJoinKeys = p.RightJoinKeys
} else {
innerJoinKeys = p.LeftJoinKeys
outerJoinKeys = p.RightJoinKeys
}
x, ok := innerChild.(*DataSource)
if !ok {
return nil
}
var tblPath *accessPath
for _, path := range x.possibleAccessPaths {
if path.isTablePath {
tblPath = path
break
}
}
if tblPath != nil && len(innerJoinKeys) == 1 {
pkCol := x.getPKIsHandleCol()
if pkCol != nil && innerJoinKeys[0].Equal(nil, pkCol) {
innerPlan := x.forceToTableScan(pkCol)
return p.constructIndexJoin(prop, innerJoinKeys, outerJoinKeys, outerIdx, innerPlan, nil, nil)
}
}
var (
bestIndexInfo *model.IndexInfo
rangesOfBest []*ranger.NewRange
maxUsedCols int
remainedOfBest []expression.Expression
keyOff2IdxOff []int
)
for _, path := range x.possibleAccessPaths {
if path.isTablePath {
continue
}
indexInfo := path.index
ranges, remained, tmpKeyOff2IdxOff := p.buildRangeForIndexJoin(indexInfo, x, innerJoinKeys)
// We choose the index by the number of used columns of the range, the much the better.
// Notice that there may be the cases like `t1.a=t2.a and b > 2 and b < 1`. So ranges can be nil though the conditions are valid.
// But obviously when the range is nil, we don't need index join.
if len(ranges) > 0 && len(ranges[0].LowVal) > maxUsedCols {
bestIndexInfo = indexInfo
maxUsedCols = len(ranges[0].LowVal)
rangesOfBest = ranges
remainedOfBest = remained
keyOff2IdxOff = tmpKeyOff2IdxOff
}
}
if bestIndexInfo != nil {
innerPlan := x.forceToIndexScan(bestIndexInfo, remainedOfBest)
return p.constructIndexJoin(prop, innerJoinKeys, outerJoinKeys, outerIdx, innerPlan, rangesOfBest, keyOff2IdxOff)
}
return nil
}
// buildRangeForIndexJoin checks whether this index can be used for building index join and return the range if this index is ok.
// If this index is invalid, just return nil range.
func (p *LogicalJoin) buildRangeForIndexJoin(indexInfo *model.IndexInfo, innerPlan *DataSource, innerJoinKeys []*expression.Column) (
[]*ranger.NewRange, []expression.Expression, []int) {
idxCols, colLengths := expression.IndexInfo2Cols(innerPlan.Schema().Columns, indexInfo)
if len(idxCols) == 0 {
return nil, nil, nil
}
conds, eqConds, keyOff2IdxOff := p.buildFakeEqCondsForIndexJoin(innerJoinKeys, idxCols, colLengths, innerPlan)
if len(keyOff2IdxOff) == 0 {
return nil, nil, nil
}
// After constant propagation, there won'be cases that t1.a=t2.a and t2.a=1 occur in the same time.
// And if there're cases like t1.a=t2.a and t1.a > 1, we can also guarantee that t1.a > 1 won't be chosen as access condition.
// So DetachCondAndBuildRangeForIndex won't miss the equal conditions we generate.
ranges, accesses, remained, _, err := ranger.DetachCondAndBuildRangeForIndex(p.ctx, conds, idxCols, colLengths)
if err != nil {
terror.Log(errors.Trace(err))
return nil, nil, nil
}
// We should guarantee that all the join's equal condition is used.
for _, eqCond := range eqConds {
if !expression.Contains(accesses, eqCond) {
return nil, nil, nil
}
}
return ranges, remained, keyOff2IdxOff
}
func (p *LogicalJoin) buildFakeEqCondsForIndexJoin(keys, idxCols []*expression.Column, colLengths []int,
innerPlan *DataSource) (accesses, eqConds []expression.Expression, keyOff2IdxOff []int) {
// Check whether all join keys match one column from index.
keyOff2IdxOff = joinKeysMatchIndex(keys, idxCols, colLengths)
if keyOff2IdxOff == nil {
return nil, nil, nil
}
// After predicate push down, the one side conditions of join must be the conditions that cannot be pushed down and
// cannot calculate range either. So we only need the innerPlan.pushedDownConds and the eq conditions that we generate.
// TODO: There may be a selection that block the index join.
conds := make([]expression.Expression, 0, len(keys)+len(innerPlan.pushedDownConds))
eqConds = make([]expression.Expression, 0, len(keys))
// Construct a fake equal expression for calculating the range.
for _, key := range keys {
// Int datum 1 can convert to all column's type(numeric type, string type, json, time type, enum, set) safely.
fakeConstant := &expression.Constant{Value: types.NewIntDatum(1), RetType: key.GetType()}
eqFunc := expression.NewFunctionInternal(p.ctx, ast.EQ, types.NewFieldType(mysql.TypeTiny), key, fakeConstant)
conds = append(conds, eqFunc)
eqConds = append(eqConds, eqFunc)
}
conds = append(conds, innerPlan.pushedDownConds...)
return conds, eqConds, keyOff2IdxOff
}
// tryToGetIndexJoin will get index join by hints. If we can generate a valid index join by hint, the second return value
// will be true, which means we force to choose this index join. Otherwise we will select a join algorithm with min-cost.
func (p *LogicalJoin) tryToGetIndexJoin(prop *requiredProp) ([]PhysicalPlan, bool) {
if len(p.EqualConditions) == 0 {
return nil, false
}
plans := make([]PhysicalPlan, 0, 2)
leftOuter := (p.preferJoinType & preferLeftAsIndexOuter) > 0
rightOuter := (p.preferJoinType & preferRightAsIndexOuter) > 0
switch p.JoinType {
case SemiJoin, AntiSemiJoin, LeftOuterSemiJoin, AntiLeftOuterSemiJoin, LeftOuterJoin:
join := p.getIndexJoinByOuterIdx(prop, 0)
if join != nil {
// If the plan is not nil and matches the hint, return it directly.
if leftOuter {
return join, true
}
plans = append(plans, join...)
}
case RightOuterJoin:
join := p.getIndexJoinByOuterIdx(prop, 1)
if join != nil {
// If the plan is not nil and matches the hint, return it directly.
if rightOuter {
return join, true
}
plans = append(plans, join...)
}
case InnerJoin:
lhsCardinality := p.Children()[0].StatsInfo().Count()
rhsCardinality := p.Children()[1].StatsInfo().Count()
leftJoins := p.getIndexJoinByOuterIdx(prop, 0)
if leftOuter && leftJoins != nil {
return leftJoins, true
}
rightJoins := p.getIndexJoinByOuterIdx(prop, 1)
if rightOuter && rightJoins != nil {
return rightJoins, true
}
if leftJoins != nil && lhsCardinality < rhsCardinality {
return leftJoins, leftOuter
}
if rightJoins != nil && rhsCardinality < lhsCardinality {
return rightJoins, rightOuter
}
plans = append(plans, leftJoins...)
plans = append(plans, rightJoins...)
}
return plans, false
}
// LogicalJoin can generates hash join, index join and sort merge join.
// Firstly we check the hint, if hint is figured by user, we force to choose the corresponding physical plan.
// If the hint is not matched, it will get other candidates.
// If the hint is not figured, we will pick all candidates.
func (p *LogicalJoin) exhaustPhysicalPlans(prop *requiredProp) []PhysicalPlan {
mergeJoins := p.getMergeJoin(prop)
if (p.preferJoinType&preferMergeJoin) > 0 && len(mergeJoins) > 0 {
return mergeJoins
}
joins := make([]PhysicalPlan, 0, 5)
joins = append(joins, mergeJoins...)
indexJoins, forced := p.tryToGetIndexJoin(prop)
if forced {
return indexJoins
}
joins = append(joins, indexJoins...)
hashJoins := p.getHashJoins(prop)
if (p.preferJoinType & preferHashJoin) > 0 {
return hashJoins
}
joins = append(joins, hashJoins...)
return joins
}
// tryToGetChildProp will check if this sort property can be pushed or not.
// When a sort column will be replaced by scalar function, we refuse it.
// When a sort column will be replaced by a constant, we just remove it.
func (p *LogicalProjection) tryToGetChildProp(prop *requiredProp) (*requiredProp, bool) {
newProp := &requiredProp{taskTp: rootTaskType, expectedCnt: prop.expectedCnt}
newCols := make([]*expression.Column, 0, len(prop.cols))
for _, col := range prop.cols {
idx := p.schema.ColumnIndex(col)
switch expr := p.Exprs[idx].(type) {
case *expression.Column:
newCols = append(newCols, expr)
case *expression.ScalarFunction:
return nil, false
}
}
newProp.cols = newCols
newProp.desc = prop.desc
return newProp, true
}
func (p *LogicalProjection) exhaustPhysicalPlans(prop *requiredProp) []PhysicalPlan {
newProp, ok := p.tryToGetChildProp(prop)
if !ok {
return nil
}
proj := PhysicalProjection{
Exprs: p.Exprs,
CalculateNoDelay: p.calculateNoDelay,
AvoidColumnEvaluator: p.avoidColumnEvaluator,
}.init(p.ctx, p.stats.scaleByExpectCnt(prop.expectedCnt), newProp)
proj.SetSchema(p.schema)
return []PhysicalPlan{proj}
}
func (lt *LogicalTopN) getPhysTopN() []PhysicalPlan {
ret := make([]PhysicalPlan, 0, 3)
for _, tp := range wholeTaskTypes {
resultProp := &requiredProp{taskTp: tp, expectedCnt: math.MaxFloat64}
topN := PhysicalTopN{
ByItems: lt.ByItems,
Count: lt.Count,
Offset: lt.Offset,
}.init(lt.ctx, lt.stats, resultProp)
ret = append(ret, topN)
}
return ret
}
func (lt *LogicalTopN) getPhysLimits() []PhysicalPlan {
prop, canPass := getPropByOrderByItems(lt.ByItems)
if !canPass {
return nil
}
ret := make([]PhysicalPlan, 0, 3)
for _, tp := range wholeTaskTypes {
resultProp := &requiredProp{taskTp: tp, expectedCnt: float64(lt.Count + lt.Offset), cols: prop.cols, desc: prop.desc}
limit := PhysicalLimit{
Count: lt.Count,
Offset: lt.Offset,
}.init(lt.ctx, lt.stats, resultProp)
ret = append(ret, limit)
}
return ret
}
func (lt *LogicalTopN) exhaustPhysicalPlans(prop *requiredProp) []PhysicalPlan {
if prop.matchItems(lt.ByItems) {
return append(lt.getPhysTopN(), lt.getPhysLimits()...)
}
return nil
}
func (la *LogicalApply) exhaustPhysicalPlans(prop *requiredProp) []PhysicalPlan {
if !prop.allColsFromSchema(la.children[0].Schema()) { // for convenient, we don't pass through any prop
return nil
}
apply := PhysicalApply{
PhysicalJoin: la.getHashJoin(prop, 1),
OuterSchema: la.corCols,
rightChOffset: la.children[0].Schema().Len(),
}.init(la.ctx,
la.stats.scaleByExpectCnt(prop.expectedCnt),
&requiredProp{expectedCnt: math.MaxFloat64, cols: prop.cols, desc: prop.desc},
&requiredProp{expectedCnt: math.MaxFloat64})
apply.SetSchema(la.schema)
return []PhysicalPlan{apply}
}
// exhaustPhysicalPlans is only for implementing interface. DataSource and Dual generate task in `findBestTask` directly.
func (p *baseLogicalPlan) exhaustPhysicalPlans(_ *requiredProp) []PhysicalPlan {
panic("This function should not be called")
}
func (la *LogicalAggregation) getStreamAggs(prop *requiredProp) []PhysicalPlan {
if len(la.possibleProperties) == 0 {
return nil
}
for _, aggFunc := range la.AggFuncs {
if aggFunc.Mode == aggregation.FinalMode {
return nil
}
}
// group by a + b is not interested in any order.
if len(la.groupByCols) != len(la.GroupByItems) {
return nil
}
streamAggs := make([]PhysicalPlan, 0, len(la.possibleProperties)*2)
for _, cols := range la.possibleProperties {
_, keys := getPermutation(cols, la.groupByCols)
if len(keys) != len(la.groupByCols) {
continue
}
for _, tp := range wholeTaskTypes {
// Second read in the double can't meet the stream aggregation's require prop.
if tp == copDoubleReadTaskType {
continue
}
childProp := &requiredProp{
taskTp: tp,
cols: keys,
desc: prop.desc,
expectedCnt: prop.expectedCnt * la.inputCount / la.stats.count,
}
if childProp.expectedCnt < prop.expectedCnt {
childProp.expectedCnt = prop.expectedCnt
}
if !prop.isPrefix(childProp) {
continue
}
agg := basePhysicalAgg{
GroupByItems: la.GroupByItems,
AggFuncs: la.AggFuncs,
}.initForStream(la.ctx, la.stats.scaleByExpectCnt(prop.expectedCnt), childProp)
agg.SetSchema(la.schema.Clone())
streamAggs = append(streamAggs, agg)
}
}
return streamAggs
}
func (la *LogicalAggregation) getHashAggs(prop *requiredProp) []PhysicalPlan {
if !prop.isEmpty() {
return nil
}
hashAggs := make([]PhysicalPlan, 0, len(wholeTaskTypes))
for _, taskTp := range wholeTaskTypes {
agg := basePhysicalAgg{
GroupByItems: la.GroupByItems,
AggFuncs: la.AggFuncs,
}.initForHash(la.ctx, la.stats.scaleByExpectCnt(prop.expectedCnt), &requiredProp{expectedCnt: math.MaxFloat64, taskTp: taskTp})
agg.SetSchema(la.schema.Clone())
hashAggs = append(hashAggs, agg)
}
return hashAggs
}
func (la *LogicalAggregation) exhaustPhysicalPlans(prop *requiredProp) []PhysicalPlan {
aggs := make([]PhysicalPlan, 0, len(la.possibleProperties)+1)
aggs = append(aggs, la.getHashAggs(prop)...)
streamAggs := la.getStreamAggs(prop)
aggs = append(aggs, streamAggs...)
return aggs
}
func (p *LogicalSelection) exhaustPhysicalPlans(prop *requiredProp) []PhysicalPlan {
sel := PhysicalSelection{
Conditions: p.Conditions,
}.init(p.ctx, p.stats.scaleByExpectCnt(prop.expectedCnt), prop)
return []PhysicalPlan{sel}
}
func (p *LogicalLimit) exhaustPhysicalPlans(prop *requiredProp) []PhysicalPlan {
if !prop.isEmpty() {
return nil
}
ret := make([]PhysicalPlan, 0, len(wholeTaskTypes))
for _, tp := range wholeTaskTypes {
resultProp := &requiredProp{taskTp: tp, expectedCnt: float64(p.Count + p.Offset)}
limit := PhysicalLimit{
Offset: p.Offset,
Count: p.Count,
}.init(p.ctx, p.stats, resultProp)
ret = append(ret, limit)
}
return ret
}
func (p *LogicalLock) exhaustPhysicalPlans(prop *requiredProp) []PhysicalPlan {
lock := PhysicalLock{
Lock: p.Lock,
}.init(p.ctx, p.stats.scaleByExpectCnt(prop.expectedCnt), prop)
return []PhysicalPlan{lock}
}
func (p *LogicalUnionAll) exhaustPhysicalPlans(prop *requiredProp) []PhysicalPlan {
// TODO: UnionAll can not pass any order, but we can change it to sort merge to keep order.
if !prop.isEmpty() {
return nil
}
chReqProps := make([]*requiredProp, 0, len(p.children))
for range p.children {
chReqProps = append(chReqProps, &requiredProp{expectedCnt: prop.expectedCnt})
}
ua := PhysicalUnionAll{}.init(p.ctx, p.stats.scaleByExpectCnt(prop.expectedCnt), chReqProps...)
return []PhysicalPlan{ua}
}
func (ls *LogicalSort) getPhysicalSort(prop *requiredProp) *PhysicalSort {
ps := PhysicalSort{ByItems: ls.ByItems}.init(ls.ctx, ls.stats.scaleByExpectCnt(prop.expectedCnt), &requiredProp{expectedCnt: math.MaxFloat64})
return ps
}
func (ls *LogicalSort) getNominalSort(reqProp *requiredProp) *NominalSort {
prop, canPass := getPropByOrderByItems(ls.ByItems)
if !canPass {
return nil
}
prop.expectedCnt = reqProp.expectedCnt
ps := NominalSort{}.init(ls.ctx, prop)
return ps
}
func (ls *LogicalSort) exhaustPhysicalPlans(prop *requiredProp) []PhysicalPlan {
if prop.matchItems(ls.ByItems) {
ret := make([]PhysicalPlan, 0, 2)
ret = append(ret, ls.getPhysicalSort(prop))
ns := ls.getNominalSort(prop)
if ns != nil {
ret = append(ret, ns)
}
return ret
}
return nil
}
func (p *LogicalExists) exhaustPhysicalPlans(prop *requiredProp) []PhysicalPlan {
if !prop.isEmpty() {
return nil
}
exists := PhysicalExists{}.init(p.ctx, p.stats, &requiredProp{expectedCnt: 1})
exists.SetSchema(p.schema)
return []PhysicalPlan{exists}
}
func (p *LogicalMaxOneRow) exhaustPhysicalPlans(prop *requiredProp) []PhysicalPlan {
if !prop.isEmpty() {
return nil
}
mor := PhysicalMaxOneRow{}.init(p.ctx, p.stats, &requiredProp{expectedCnt: 2})
return []PhysicalPlan{mor}
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/pingcap/tidb.git
git@gitee.com:pingcap/tidb.git
pingcap
tidb
tidb
v2.0.11

搜索帮助