tidb flat_plan 源码

2022-09-19 浏览 (877)

tidb flat_plan 代码

文件路径:/planner/core/flat_plan.go

/ Copyright 2022 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 (
	"github.com/pingcap/tidb/kv"
	"github.com/pingcap/tidb/util/texttree"
)

/ FlatPhysicalPlan provides an easier structure to traverse a plan and collect needed information.
/ Note: Although it's named FlatPhysicalPlan, there also could be Insert, Delete and Update at the beginning of Main.
type FlatPhysicalPlan struct {
	Main FlatPlanTree
	CTEs []FlatPlanTree

	/ InExecute and InExplain are expected to handle some special cases. Usually you don't need to use them.

	/ InExecute means if the original plan tree contains Execute operator.
	/
	/ Be careful when trying to use this, InExecute is true doesn't mean we are handling an EXECUTE statement.
	/ When collecting information from the plan in an EXECUTE statement, usually we directly use the plan
	/ in Execute.Plan, not Execute itself, so InExecute will be false.
	/
	/ When will InExecute be true? When you're using "EXPLAIN FOR CONNECTION" to get the last plan of
	/ a connection (usually we will record Explain.TargetPlan for an EXPLAIN statement) and that plan
	/ is from an EXECUTE statement, we will collect from Execute itself, not directly from Execute.Plan,
	/ then InExecute will be true.
	InExecute bool

	/ InExplain means if the original plan tree contains Explain operator.
	InExplain bool

	/ The fields below are only used when building the FlatPhysicalPlan.
	buildSideFirst bool
	ctesToFlatten  []*PhysicalCTE
}

/ FlatPlanTree is a simplified plan tree.
/ It arranges all operators in the tree as a slice, ordered by the order of traversing the tree, which means a
/ depth-first traversal plus some special rule for some operators.
type FlatPlanTree []*FlatOperator

/ GetSelectPlan skips Insert, Delete and Update at the beginning of the FlatPlanTree.
/ Note:
/
/	It returns a reference to the original FlatPlanTree, please avoid modifying the returned value.
/	Since you get a part of the original slice, you need to adjust the FlatOperator.Depth and FlatOperator.ChildrenIdx when using them.
func (e FlatPlanTree) GetSelectPlan() FlatPlanTree {
	if len(e) == 0 {
		return nil
	}
	for i, op := range e {
		switch op.Origin.(type) {
		case *Insert, *Delete, *Update:
		default:
			return e[i:]
		}
	}
	return nil
}

/ FlatOperator is a simplified operator.
/ It contains a reference to the original operator and some usually needed information.
type FlatOperator struct {
	/ A reference to the original operator.
	Origin Plan

	/ With ChildrenIdx and ChildrenEndIdx, we can locate every children subtrees of this operator in the FlatPlanTree.
	/ For example, the first children subtree is flatTree[ChildrenIdx[0] : ChildrenIdx[1]], the last children subtree
	/ is flatTree[ChildrenIdx[n-1] : ChildrenEndIdx].

	/ ChildrenIdx is the indexes of the children of this operator in the FlatPlanTree.
	/ It's ordered from small to large.
	ChildrenIdx []int
	/ ChildrenEndIdx is the index of the last operator of children subtrees of this operator in the FlatPlanTree.
	ChildrenEndIdx int

	/ NeedReverseDriverSide means if we need to reverse the order of children to keep build side before probe side.
	/
	/ Specifically, it means if the below are all true:
	/ 1. this operator has two children
	/ 2. the first child's Label is the probe side and the second's is the build side.
	/
	/ If you call FlattenPhysicalPlan with buildSideFirst true, NeedReverseDriverSide will be useless.
	NeedReverseDriverSide bool

	Depth     uint32
	Label     OperatorLabel
	IsRoot    bool
	StoreType kv.StoreType
	/ ReqType is only meaningful when IsRoot is false.
	ReqType ReadReqType

	/ The below two fields are mainly for text tree formatting. See texttree.PrettyIdentifier().
	TextTreeIndent string
	IsLastChild    bool

	IsPhysicalPlan bool
}

/ OperatorLabel acts as some additional information to the name, usually it means its relationship with its parent.
/ It's useful for index join, apply, index lookup, cte and so on.
type OperatorLabel uint8

const (
	/ Empty means OperatorLabel is meaningless for this operator.
	Empty OperatorLabel = iota
	/ BuildSide means this operator is at the build side of its parent
	BuildSide
	/ ProbeSide means this operator is at the probe side of its parent
	ProbeSide
	/ SeedPart means this operator is the seed part of its parent (a cte)
	SeedPart
	/ RecursivePart means this operator is the recursive part of its parent (a cte)
	RecursivePart
)

func (d OperatorLabel) String() string {
	switch d {
	case Empty:
		return ""
	case BuildSide:
		return "(Build)"
	case ProbeSide:
		return "(Probe)"
	case SeedPart:
		return "(Seed Part)"
	case RecursivePart:
		return "(Recursive Part)"
	}
	return ""
}

type operatorCtx struct {
	depth       uint32
	label       OperatorLabel
	isRoot      bool
	storeType   kv.StoreType
	reqType     ReadReqType
	indent      string
	isLastChild bool
}

/ FlattenPhysicalPlan generates a FlatPhysicalPlan from a PhysicalPlan, Insert, Delete, Update, Explain or Execute.
func FlattenPhysicalPlan(p Plan, buildSideFirst bool) *FlatPhysicalPlan {
	if p == nil {
		return nil
	}
	res := &FlatPhysicalPlan{
		buildSideFirst: buildSideFirst,
	}
	initInfo := &operatorCtx{
		depth:       0,
		label:       Empty,
		isRoot:      true,
		storeType:   kv.TiDB,
		indent:      "",
		isLastChild: true,
	}
	res.Main, _ = res.flattenRecursively(p, initInfo, nil)

	flattenedCTEPlan := make(map[int]struct{}, len(res.ctesToFlatten))

	/ Note that ctesToFlatten may be modified during the loop, so we manually loop over it instead of using for...range.
	for i := 0; i < len(res.ctesToFlatten); i++ {
		cte := res.ctesToFlatten[i]
		cteDef := (*CTEDefinition)(cte)
		if _, ok := flattenedCTEPlan[cteDef.CTE.IDForStorage]; ok {
			continue
		}
		cteExplained := res.flattenCTERecursively(cteDef, initInfo, nil)
		res.CTEs = append(res.CTEs, cteExplained)
		flattenedCTEPlan[cteDef.CTE.IDForStorage] = struct{}{}
	}
	return res
}

func (f *FlatPhysicalPlan) flattenSingle(p Plan, info *operatorCtx) *FlatOperator {
	/ Some operators are not initialized and given an ExplainID. So their explain IDs are "_0"
	/ (when in EXPLAIN FORMAT = 'brief' it will be ""), we skip such operators.
	/ Examples: Explain, Execute
	if len(p.TP()) == 0 && p.ID() == 0 {
		return nil
	}
	res := &FlatOperator{
		Origin:         p,
		Label:          info.label,
		IsRoot:         info.isRoot,
		StoreType:      info.storeType,
		Depth:          info.depth,
		ReqType:        info.reqType,
		TextTreeIndent: info.indent,
		IsLastChild:    info.isLastChild,
	}

	if _, ok := p.(PhysicalPlan); ok {
		res.IsPhysicalPlan = true
	}
	return res
}

/ Note that info should not be modified in this method.
func (f *FlatPhysicalPlan) flattenRecursively(p Plan, info *operatorCtx, target FlatPlanTree) (res FlatPlanTree, idx int) {
	idx = -1
	flat := f.flattenSingle(p, info)
	if flat != nil {
		target = append(target, flat)
		idx = len(target) - 1
	}
	childIdxs := make([]int, 0)
	var childIdx int
	childCtx := &operatorCtx{
		depth:     info.depth + 1,
		isRoot:    info.isRoot,
		storeType: info.storeType,
		reqType:   info.reqType,
		indent:    texttree.Indent4Child(info.indent, info.isLastChild),
	}
	/ For physical operators, we just enumerate their children and collect their information.
	/ Note that some physical operators are special, and they are handled below this part.
	if physPlan, ok := p.(PhysicalPlan); ok {
		label := make([]OperatorLabel, len(physPlan.Children()))

		switch plan := physPlan.(type) {
		case *PhysicalApply:
			label[plan.InnerChildIdx] = ProbeSide
			label[1-plan.InnerChildIdx] = BuildSide
		case *PhysicalHashJoin:
			if plan.UseOuterToBuild {
				label[plan.InnerChildIdx] = ProbeSide
				label[1-plan.InnerChildIdx] = BuildSide
			} else {
				label[plan.InnerChildIdx] = BuildSide
				label[1-plan.InnerChildIdx] = ProbeSide
			}
		case *PhysicalMergeJoin:
			if plan.JoinType == RightOuterJoin {
				label[0] = BuildSide
				label[1] = ProbeSide
			} else {
				label[0] = ProbeSide
				label[1] = BuildSide
			}
		case *PhysicalIndexJoin:
			label[plan.InnerChildIdx] = ProbeSide
			label[1-plan.InnerChildIdx] = BuildSide
		case *PhysicalIndexMergeJoin:
			label[plan.InnerChildIdx] = ProbeSide
			label[1-plan.InnerChildIdx] = BuildSide
		case *PhysicalIndexHashJoin:
			label[plan.InnerChildIdx] = ProbeSide
			label[1-plan.InnerChildIdx] = BuildSide
		}

		children := make([]PhysicalPlan, len(physPlan.Children()))
		copy(children, physPlan.Children())
		if len(label) == 2 &&
			label[0] == ProbeSide &&
			label[1] == BuildSide {
			if f.buildSideFirst {
				/ Put the build side before the probe side if buildSideFirst is true.
				label[0], label[1] = label[1], label[0]
				children[0], children[1] = children[1], children[0]
			} else if flat != nil {
				/ Set NeedReverseDriverSide to true if buildSideFirst is false.
				flat.NeedReverseDriverSide = true
			}
		}

		for i := range children {
			childCtx.label = label[i]
			childCtx.isLastChild = i == len(children)-1
			target, childIdx = f.flattenRecursively(children[i], childCtx, target)
			childIdxs = append(childIdxs, childIdx)
		}
	}

	/ For part of physical operators and some special operators, we need some special logic to get their "children".
	/ For PhysicalCTE, we need to add the plan tree into flatTree.ctesToFlatten.
	switch plan := p.(type) {
	case *PhysicalTableReader:
		childCtx.isRoot = false
		childCtx.storeType = plan.StoreType
		childCtx.reqType = plan.ReadReqType
		childCtx.label = Empty
		childCtx.isLastChild = true
		target, childIdx = f.flattenRecursively(plan.tablePlan, childCtx, target)
		childIdxs = append(childIdxs, childIdx)
	case *PhysicalIndexReader:
		childCtx.isRoot = false
		childCtx.reqType = Cop
		childCtx.storeType = kv.TiKV
		childCtx.label = Empty
		childCtx.isLastChild = true
		target, childIdx = f.flattenRecursively(plan.indexPlan, childCtx, target)
		childIdxs = append(childIdxs, childIdx)
	case *PhysicalIndexLookUpReader:
		childCtx.isRoot = false
		childCtx.reqType = Cop
		childCtx.storeType = kv.TiKV
		childCtx.label = BuildSide
		childCtx.isLastChild = false
		target, childIdx = f.flattenRecursively(plan.indexPlan, childCtx, target)
		childIdxs = append(childIdxs, childIdx)
		childCtx.label = ProbeSide
		childCtx.isLastChild = true
		target, childIdx = f.flattenRecursively(plan.tablePlan, childCtx, target)
		childIdxs = append(childIdxs, childIdx)
	case *PhysicalIndexMergeReader:
		childCtx.isRoot = false
		childCtx.reqType = Cop
		childCtx.storeType = kv.TiKV
		for _, pchild := range plan.partialPlans {
			childCtx.label = BuildSide
			childCtx.isLastChild = false
			target, childIdx = f.flattenRecursively(pchild, childCtx, target)
			childIdxs = append(childIdxs, childIdx)
		}
		childCtx.label = ProbeSide
		childCtx.isLastChild = true
		target, childIdx = f.flattenRecursively(plan.tablePlan, childCtx, target)
		childIdxs = append(childIdxs, childIdx)
	case *PhysicalShuffleReceiverStub:
		childCtx.isRoot = true
		childCtx.label = Empty
		childCtx.isLastChild = true
		target, childIdx = f.flattenRecursively(plan.DataSource, childCtx, target)
		childIdxs = append(childIdxs, childIdx)
	case *PhysicalCTE:
		f.ctesToFlatten = append(f.ctesToFlatten, plan)
	case *Insert:
		if plan.SelectPlan != nil {
			childCtx.isRoot = true
			childCtx.label = Empty
			childCtx.isLastChild = true
			target, childIdx = f.flattenRecursively(plan.SelectPlan, childCtx, target)
			childIdxs = append(childIdxs, childIdx)
		}
	case *Update:
		if plan.SelectPlan != nil {
			childCtx.isRoot = true
			childCtx.label = Empty
			childCtx.isLastChild = true
			target, childIdx = f.flattenRecursively(plan.SelectPlan, childCtx, target)
			childIdxs = append(childIdxs, childIdx)
		}
	case *Delete:
		if plan.SelectPlan != nil {
			childCtx.isRoot = true
			childCtx.label = Empty
			childCtx.isLastChild = true
			target, childIdx = f.flattenRecursively(plan.SelectPlan, childCtx, target)
			childIdxs = append(childIdxs, childIdx)
		}
	case *Execute:
		f.InExecute = true
		if plan.Plan != nil {
			childCtx.isRoot = true
			childCtx.indent = info.indent
			childCtx.label = Empty
			childCtx.isLastChild = true
			target, childIdx = f.flattenRecursively(plan.Plan, childCtx, target)
			childIdxs = append(childIdxs, childIdx)
		}
	case *Explain:
		f.InExplain = true
		/ Explain is ignored in flattenSingle(). We start to explain its TargetPlan from a new operatorCtx.
		if plan.TargetPlan != nil {
			initInfo := &operatorCtx{
				depth:       0,
				label:       Empty,
				isRoot:      true,
				storeType:   kv.TiDB,
				indent:      "",
				isLastChild: true,
			}
			target, childIdx = f.flattenRecursively(plan.TargetPlan, initInfo, target)
			childIdxs = append(childIdxs, childIdx)
		}
	}
	if flat != nil {
		flat.ChildrenIdx = childIdxs
		flat.ChildrenEndIdx = len(target) - 1
	}
	return target, idx
}

func (f *FlatPhysicalPlan) flattenCTERecursively(cteDef *CTEDefinition, info *operatorCtx, target FlatPlanTree) FlatPlanTree {
	flat := f.flattenSingle(cteDef, info)
	if flat != nil {
		target = append(target, flat)
	}
	childIdxs := make([]int, 0)
	var childIdx int
	childInfo := &operatorCtx{
		depth:       info.depth + 1,
		label:       SeedPart,
		isRoot:      true,
		storeType:   kv.TiDB,
		indent:      texttree.Indent4Child(info.indent, info.isLastChild),
		isLastChild: cteDef.RecurPlan == nil,
	}
	target, childIdx = f.flattenRecursively(cteDef.SeedPlan, childInfo, target)
	childIdxs = append(childIdxs, childIdx)
	if cteDef.RecurPlan != nil {
		childInfo.label = RecursivePart
		childInfo.isLastChild = true
		target, childIdx = f.flattenRecursively(cteDef.RecurPlan, childInfo, target)
		childIdxs = append(childIdxs, childIdx)
	}
	if flat != nil {
		flat.ChildrenIdx = childIdxs
	}
	return target
}

相关信息

tidb 源码目录

相关文章

tidb access_object 源码

tidb collect_column_stats_usage 源码

tidb common_plans 源码

tidb encode 源码

tidb errors 源码

tidb exhaust_physical_plans 源码

tidb explain 源码

tidb expression_rewriter 源码

tidb find_best_task 源码

tidb fragment 源码

^