TiDB 源码阅读 逻辑优化

logicalOptimize

在这里插入图片描述

func logicalOptimize(ctx context.Context, flag uint64, logic LogicalPlan) (LogicalPlan, error) {
	var err error
	for i, rule := range optRuleList {
		// The order of flags is same as the order of optRule in the list.
		// We use a bitmask to record which opt rules should be used. If the i-th bit is 1, it means we should
		// apply i-th optimizing rule.
		if flag&(1<<uint(i)) == 0 || isLogicalRuleDisabled(rule) {
			continue
		}
		logic, err = rule.optimize(ctx, logic)
		if err != nil {
			return nil, err
		}
	}
	return logic, err
}

var optRuleList = []logicalOptRule{
	&gcSubstituter{},
	&columnPruner{},
	&resultReorder{},
	&buildKeySolver{},
	&decorrelateSolver{},
	&aggregationEliminator{},
	&projectionEliminator{},
	&maxMinEliminator{},
	&ppdSolver{},
	&outerJoinEliminator{},
	&partitionProcessor{},
	&aggregationPushDownSolver{},
	&pushDownTopNOptimizer{},
	&joinReOrderSolver{},
	&columnPruner{}, // column pruning again at last, note it will mess up the results of buildKeySolver
}

columnPruner

type columnPruner struct {
}

func (s *columnPruner) optimize(ctx context.Context, lp LogicalPlan) (LogicalPlan, error) {
	err := lp.PruneColumns(lp.Schema().Columns)
	return lp, err
}

LogicalPlan 需要实现 PruneColumns([]*expression.Column) error 接口

// LogicalPlan is a tree of logical operators.
// We can do a lot of logical optimizations to it, like predicate pushdown and column pruning.
type LogicalPlan interface {
	...
	// PruneColumns prunes the unused columns.
	PruneColumns([]*expression.Column) error
	...
}

例子

create table t(a int, b int, c int);
create table s(a int , b int, c int);
explain select t.a, s.* from t join (select a, count(*), sum(b) from s group by a) s on t.a = s.a where t.a < substring('123', 1, 1);
+------------------------------+----------+-----------+---------------+------------------------------------------------------------------------------------------------------------------------+
| id                           | estRows  | task      | access object | operator info                                                                                                          |
+------------------------------+----------+-----------+---------------+------------------------------------------------------------------------------------------------------------------------+
| Projection_10                | 3323.33  | root      |               | myc_test.t.a, myc_test.s.a, Column#9, Column#10                                                                        |
| └─HashJoin_11                | 3323.33  | root      |               | inner join, equal:[eq(myc_test.t.a, myc_test.s.a)]                                                                     |
|   ├─HashAgg_18(Build)        | 2658.67  | root      |               | group by:Column#16, funcs:count(1)->Column#9, funcs:sum(Column#14)->Column#10, funcs:firstrow(Column#15)->myc_test.s.a |
|   │ └─Projection_26          | 3323.33  | root      |               | cast(myc_test.s.b, decimal(32,0) BINARY)->Column#14, myc_test.s.a, myc_test.s.a                                        |
|   │   └─TableReader_25       | 3323.33  | root      |               | data:Selection_24                                                                                                      |
|   │     └─Selection_24       | 3323.33  | cop[tikv] |               | lt(myc_test.s.a, 1), not(isnull(myc_test.s.a))                                                                         |
|   │       └─TableFullScan_23 | 10000.00 | cop[tikv] | table:s       | keep order:false, stats:pseudo                                                                                         |
|   └─TableReader_15(Probe)    | 3323.33  | root      |               | data:Selection_14                                                                                                      |
|     └─Selection_14           | 3323.33  | cop[tikv] |               | lt(myc_test.t.a, 1), not(isnull(myc_test.t.a))                                                                         |
|       └─TableFullScan_13     | 10000.00 | cop[tikv] | table:t       | keep order:false, stats:pseudo                                                                                         |
+------------------------------+----------+-----------+---------------+------------------------------------------------------------------------------------------------------------------------+

*LogicalProjection.PruneColumns

// PruneColumns implements LogicalPlan interface.
// If any expression has SetVar function or Sleep function, we do not prune it.
func (p *LogicalProjection) PruneColumns(parentUsedCols []*expression.Column) error {
	child := p.children[0]
// 返回一个 make([]bool, p.schema.Len()),
// 对于 p.schema 中的每一个 col,如果出现在 parentUsedCols,那么标记为 true 
// 标记为 true 的个数,应该等于 len(parentUsedCols)
	used := expression.GetUsedList(parentUsedCols, p.schema)

	for i := len(used) - 1; i >= 0; i-- {
		if !used[i] && !exprHasSetVarOrSleep(p.Exprs[i]) {
// 更新 p.schema.Columns(LogicalProjection 的输出列)为在 parentUsedCols 中出现的列
// or has SetVar function or Sleep function
// 每个 p.schema.Column 对应一个 p.Expr 所以同时要更新
			p.schema.Columns = append(p.schema.Columns[:i], p.schema.Columns[i+1:]...)
			p.Exprs = append(p.Exprs[:i], p.Exprs[i+1:]...)
		}
	}

// 从 p.Exprs 中提取出 p 自己所需要的 coloum
// 如果 p.Expr 是 expression.*Column,那么就返回这个
// 如果 p.Expr 是 expression.*ScalarFunction,那么需要从 args 里面提取 coloumn
	selfUsedCols := make([]*expression.Column, 0, len(p.Exprs))
	selfUsedCols = expression.ExtractColumnsFromExpressions(selfUsedCols, p.Exprs, nil)
	return child.PruneColumns(selfUsedCols)
}

*LogicalSelection.PruneColumns

// PruneColumns implements LogicalPlan interface.
func (p *LogicalSelection) PruneColumns(parentUsedCols []*expression.Column) error {
	child := p.children[0]
// parentUsedCols + p.Conditions 中所需要的 cols 就是 p 自己所需要的 cols 
	parentUsedCols = expression.ExtractColumnsFromExpressions(parentUsedCols, p.Conditions, nil)
	return child.PruneColumns(parentUsedCols)
}

可以看到,LogicalSelectionSchema() 就是他孩子的 Schema()LogicalSelection 就只是一个过滤的作用

// LogicalSelection represents a where or having predicate.
type LogicalSelection struct {
	baseLogicalPlan

	// Originally the WHERE or ON condition is parsed into a single expression,
	// but after we converted to CNF(Conjunctive normal form), it can be
	// split into a list of AND conditions.
	Conditions []expression.Expression

	// having selection can't be pushed down, because it must above the aggregation.
	buildByHaving bool
}

// Schema implements Plan Schema interface.
func (p *baseLogicalPlan) Schema() *expression.Schema {
	return p.children[0].Schema()
}

*LogicalJoin.PruneColumns

// PruneColumns implements LogicalPlan interface.
func (p *LogicalJoin) PruneColumns(parentUsedCols []*expression.Column) error {
// parentUsedCols + p.EqualConditions + p.LeftConditions + p.RightConditions + p.OtherConditions
// 中的 cols 中属于 lChild.Schema() 的 col 是 lChild 所需要产出的 col 即 leftCols
// rightCols 同理
	leftCols, rightCols := p.extractUsedCols(parentUsedCols)

	err := p.children[0].PruneColumns(leftCols)
	if err != nil {
		return err
	}
	addConstOneForEmptyProjection(p.children[0])

	err = p.children[1].PruneColumns(rightCols)
	if err != nil {
		return err
	}
	addConstOneForEmptyProjection(p.children[1])

	p.mergeSchema()
// ???
	if p.JoinType == LeftOuterSemiJoin || p.JoinType == AntiLeftOuterSemiJoin {
		joinCol := p.schema.Columns[len(p.schema.Columns)-1]
		parentUsedCols = append(parentUsedCols, joinCol)
	}
	p.inlineProjection(parentUsedCols)
	return nil
}
// By add const one, we can avoid empty Projection is eliminated.
// Because in some cases, Projectoin cannot be eliminated even its output is empty.
func addConstOneForEmptyProjection(p LogicalPlan) {
	proj, ok := p.(*LogicalProjection)
	if !ok {
		return
	}
	if proj.Schema().Len() != 0 {
		return
	}

// 这里可以看出 LogicalProjection 的真实作用,proj.Exprs 中的每个 expr 的结果映射为 proj.schema 中的一个 col
	constOne := expression.NewOne()
	proj.schema.Append(&expression.Column{
		UniqueID: proj.ctx.GetSessionVars().AllocPlanColumnID(),
		RetType:  constOne.GetType(),
	})
	proj.Exprs = append(proj.Exprs, &expression.Constant{
		Value:   constOne.Value,
		RetType: constOne.GetType(),
	})
}
func (p *LogicalJoin) mergeSchema() {
	p.schema = buildLogicalJoinSchema(p.JoinType, p)
}

// ??? 
func buildLogicalJoinSchema(joinType JoinType, join LogicalPlan) *expression.Schema {
	leftSchema := join.Children()[0].Schema()
	switch joinType {
	case SemiJoin, AntiSemiJoin:
		return leftSchema.Clone()
	case LeftOuterSemiJoin, AntiLeftOuterSemiJoin:
		newSchema := leftSchema.Clone()
		newSchema.Append(join.Schema().Columns[join.Schema().Len()-1])
		return newSchema
	}
	newSchema := expression.MergeSchema(leftSchema, join.Children()[1].Schema())
	if joinType == LeftOuterJoin {
		resetNotNullFlag(newSchema, leftSchema.Len(), newSchema.Len())
	} else if joinType == RightOuterJoin {
		resetNotNullFlag(newSchema, 0, leftSchema.Len())
	}
	return newSchema
}

// inlineProjection prunes unneeded columns inline a executor.
func (s *logicalSchemaProducer) inlineProjection(parentUsedCols []*expression.Column) {
// 只保留属于 parentUsedCols 的 col
	used := expression.GetUsedList(parentUsedCols, s.Schema())
	for i := len(used) - 1; i >= 0; i-- {
		if !used[i] {
			s.schema.Columns = append(s.Schema().Columns[:i], s.Schema().Columns[i+1:]...)
		}
	}
}

*DataSource.PruneColumns

// PruneColumns implements LogicalPlan interface.
func (ds *DataSource) PruneColumns(parentUsedCols []*expression.Column) error {
// ds.schema 在没有 PruneColumns 之前会是这个表的所有列
	used := expression.GetUsedList(parentUsedCols, ds.schema)
// ds.allConds 是推下来的谓词
	exprCols := expression.ExtractColumnsFromExpressions(nil, ds.allConds, nil)
	exprUsed := expression.GetUsedList(exprCols, ds.schema)

	originSchemaColumns := ds.schema.Columns
	originColumns := ds.Columns
	for i := len(used) - 1; i >= 0; i-- {
// 更新 ds.schema.Columns(DataSource 的输出列)为在 parentUsedCols 中出现的列
// or 在 ds.allConds 中使用到的列
// 每个 ds.schema.Column 对应一个 ds.Column 所以同时要更新
		if !used[i] && !exprUsed[i] {
			ds.schema.Columns = append(ds.schema.Columns[:i], ds.schema.Columns[i+1:]...)
			ds.Columns = append(ds.Columns[:i], ds.Columns[i+1:]...)
		}
	}
	
	// For SQL like `select 1 from t`, tikv's response will be empty if no column is in schema.
	// So we'll force to push one if schema doesn't have any column.
/// ???
	if ds.schema.Len() == 0 {
		...
	}
	
// ds.handleCols 是表的主键列(主键可能是一列,也可能是多列组成,如果表没有主键,会送你一个)
// 见 func (b *PlanBuilder) buildDataSource
	if ds.handleCols != nil && ds.handleCols.IsInt() && ds.schema.ColumnIndex(ds.handleCols.GetCol(0)) == -1 {
		ds.handleCols = nil
	}
	return nil
}

*LogicalAggregation.PruneColumns

func (la *LogicalAggregation) PruneColumns(parentUsedCols []*expression.Column) error {
	child := la.children[0]
	used := expression.GetUsedList(parentUsedCols, la.Schema())
// la.Schema() 中的每一个 col 和 la.AggFuncs 中的 AggFun 一一对应
// 这说明在未列剪枝前 la.GroupByItems 也在 la.Schema() 中
// 且 对应的 AggFunc.Name = ast.AggFuncFirstRow
	allFirstRow := true
	allRemainFirstRow := true
	for i := len(used) - 1; i >= 0; i-- {
		if la.AggFuncs[i].Name != ast.AggFuncFirstRow {
			allFirstRow = false
		}
// 去除 la.schema.Columns 中不在 parentUsedCols 中的 col
// 并去除对应的 AggFunc
						// exprHasSetVarOrSleep checks if the expression has SetVar function or Sleep function.
		if !used[i] && !ExprsHasSideEffects(la.AggFuncs[i].Args) {
			la.schema.Columns = append(la.schema.Columns[:i], la.schema.Columns[i+1:]...)
			la.AggFuncs = append(la.AggFuncs[:i], la.AggFuncs[i+1:]...)
		} else if la.AggFuncs[i].Name != ast.AggFuncFirstRow {
			allRemainFirstRow = false
		}
	}
	
	var selfUsedCols []*expression.Column
// la.AggFuncs 中使用到的 col 是 selfUsedCols
	for _, aggrFunc := range la.AggFuncs {
		selfUsedCols = expression.ExtractColumnsFromExpressions(selfUsedCols, aggrFunc.Args, nil)

		var cols []*expression.Column
		aggrFunc.OrderByItems, cols = pruneByItems(aggrFunc.OrderByItems)
		selfUsedCols = append(selfUsedCols, cols...)
	}
	
// ???
	if len(la.AggFuncs) == 0 || (!allFirstRow && allRemainFirstRow) {
		// If all the aggregate functions are pruned, we should add an aggregate function to maintain the info of row numbers.
		// For all the aggregate functions except `first_row`, if we have an empty table defined as t(a,b),
		// `select agg(a) from t` would always return one row, while `select agg(a) from t group by b` would return empty.
		// For `first_row` which is only used internally by tidb, `first_row(a)` would always return empty for empty input now.
		var err error
		var newAgg *aggregation.AggFuncDesc
		if allFirstRow {
			newAgg, err = aggregation.NewAggFuncDesc(la.ctx, ast.AggFuncFirstRow, []expression.Expression{expression.NewOne()}, false)
		} else {
			newAgg, err = aggregation.NewAggFuncDesc(la.ctx, ast.AggFuncCount, []expression.Expression{expression.NewOne()}, false)
		}
		if err != nil {
			return err
		}
		la.AggFuncs = append(la.AggFuncs, newAgg)
		col := &expression.Column{
			UniqueID: la.ctx.GetSessionVars().AllocPlanColumnID(),
			RetType:  newAgg.RetTp,
		}
		la.schema.Columns = append(la.schema.Columns, col)
	}
// la.GroupByItems 中使用到的 col 也属于 selfUsedCols
	if len(la.GroupByItems) > 0 {
		for i := len(la.GroupByItems) - 1; i >= 0; i-- {
			cols := expression.ExtractColumns(la.GroupByItems[i])
			if len(cols) == 0 && !exprHasSetVarOrSleep(la.GroupByItems[i]) {
				la.GroupByItems = append(la.GroupByItems[:i], la.GroupByItems[i+1:]...)
			} else {
				selfUsedCols = append(selfUsedCols, cols...)
			}
		}
		// If all the group by items are pruned, we should add a constant 1 to keep the correctness.
		// Because `select count(*) from t` is different from `select count(*) from t group by 1`.
// ???
		if len(la.GroupByItems) == 0 {
			la.GroupByItems = []expression.Expression{expression.NewOne()}
		}
	}
	return child.PruneColumns(selfUsedCols)
}

ppdSolver

type ppdSolver struct{}

func (s *ppdSolver) optimize(ctx context.Context, lp LogicalPlan) (LogicalPlan, error) {
	_, p := lp.PredicatePushDown(nil)
	return p, nil
}

LogicalPlan 需要实现 PredicatePushDown([]expression.Expression) ([]expression.Expression, LogicalPlan) 接口

// LogicalPlan is a tree of logical operators.
// We can do a lot of logical optimizations to it, like predicate pushdown and column pruning.
type LogicalPlan interface {
	...
	// PredicatePushDown pushes down the predicates in the where/on/having clauses as deeply as possible.
	// It will accept a predicate that is an expression slice, and return the expressions that can't be pushed.
	// Because it might change the root if the having clause exists, we need to return a plan that represents a new root.
	PredicatePushDown([]expression.Expression) ([]expression.Expression, LogicalPlan)
	...
}
    

例子

create table t(a int, b int, c int);
create table s(a int , b int, c int);
explain select t.a, s.* from t join (select a, count(*), sum(b) from s group by a) s on t.a = s.a where t.a < substring('123', 1, 1);
+------------------------------+----------+-----------+---------------+------------------------------------------------------------------------------------------------------------------------+
| id                           | estRows  | task      | access object | operator info                                                                                                          |
+------------------------------+----------+-----------+---------------+------------------------------------------------------------------------------------------------------------------------+
| Projection_10                | 3323.33  | root      |               | myc_test.t.a, myc_test.s.a, Column#9, Column#10                                                                        |
| └─HashJoin_11                | 3323.33  | root      |               | inner join, equal:[eq(myc_test.t.a, myc_test.s.a)]                                                                     |
|   ├─HashAgg_18(Build)        | 2658.67  | root      |               | group by:Column#16, funcs:count(1)->Column#9, funcs:sum(Column#14)->Column#10, funcs:firstrow(Column#15)->myc_test.s.a |
|   │ └─Projection_26          | 3323.33  | root      |               | cast(myc_test.s.b, decimal(32,0) BINARY)->Column#14, myc_test.s.a, myc_test.s.a                                        |
|   │   └─TableReader_25       | 3323.33  | root      |               | data:Selection_24                                                                                                      |
|   │     └─Selection_24       | 3323.33  | cop[tikv] |               | lt(myc_test.s.a, 1), not(isnull(myc_test.s.a))                                                                         |
|   │       └─TableFullScan_23 | 10000.00 | cop[tikv] | table:s       | keep order:false, stats:pseudo                                                                                         |
|   └─TableReader_15(Probe)    | 3323.33  | root      |               | data:Selection_14                                                                                                      |
|     └─Selection_14           | 3323.33  | cop[tikv] |               | lt(myc_test.t.a, 1), not(isnull(myc_test.t.a))                                                                         |
|       └─TableFullScan_13     | 10000.00 | cop[tikv] | table:t       | keep order:false, stats:pseudo                                                                                         |
+------------------------------+----------+-----------+---------------+------------------------------------------------------------------------------------------------------------------------+

*LogicalProjection.PredicatePushDown

// PredicatePushDown implements LogicalPlan PredicatePushDown interface.
func (p *LogicalProjection) PredicatePushDown(predicates []expression.Expression) (ret []expression.Expression, retPlan LogicalPlan) {
	canBePushed := make([]expression.Expression, 0, len(predicates))
	canNotBePushed := make([]expression.Expression, 0, len(predicates))
	for _, expr := range p.Exprs {
		// 如果 an expression contains SetVar function and assign a value
		// 直接不推 predicates 了
		if expression.HasAssignSetVarFunc(expr) {
			
			_, child := p.baseLogicalPlan.PredicatePushDown(nil)
			return predicates, child
		}
	}
	
// ??? 感觉像是把映射后的 col 转换成 映射前的 col
	for _, cond := range predicates {
		newFilter := expression.ColumnSubstitute(cond, p.Schema(), p.Exprs)
		if !expression.HasGetSetVarFunc(newFilter) {
			canBePushed = append(canBePushed, newFilter)
		} else {
			canNotBePushed = append(canNotBePushed, cond)
		}
	}
	
	remained, child := p.baseLogicalPlan.PredicatePushDown(canBePushed)
	return append(remained, canNotBePushed...), child
}

*baseLogicalPlan.PredicatePushDown

// PredicatePushDown implements LogicalPlan interface.
// 只要有孩子那就全力往下推,推不下去就在孩子头上加 LogicalSelection
// 没孩子那就没办法了,送回上层
func (p *baseLogicalPlan) PredicatePushDown(predicates []expression.Expression) ([]expression.Expression, LogicalPlan) {
	if len(p.children) == 0 {
// p 没有孩子了,不推 predicates 了,返回 p.self
		return predicates, p.self
	}
	child := p.children[0]
// 将 predicates 推到孩子上
// rest 推不下去的生成一个 LogicalSelection 接到孩子的头上,作为 p 的新孩子
	rest, newChild := child.PredicatePushDown(predicates)
	addSelection(p.self, newChild, rest, 0)
	return nil, p.self
}

func addSelection(p LogicalPlan, child LogicalPlan, conditions []expression.Expression, chIdx int) {
// 没有 conditions 直接啥都不用干
	if len(conditions) == 0 {
		p.Children()[chIdx] = child
		return
	}
// 将 conditions 进行一些改写,比如:
// a = d & b * 2 = c & b = 1 & a = 4 => d = 4 & 2 = c & b = 1 & a = 4
// a = b and b = c and c = d and c < 1 => a = b and b = c and c = d and c < 1 and a < 1 and b < 1 and d < 1
	conditions = expression.PropagateConstant(p.SCtx(), conditions)

// 如果 conditions 中有常量 false	,那么用 TableDual 作为 p 的孩子
	// Return table dual when filter is constant false or null.
	dual := Conds2TableDual(child, conditions)
	if dual != nil {
		p.Children()[chIdx] = dual
		return
	}

// 删除掉常量为 true 的 cond
	conditions = DeleteTrueExprs(p, conditions)
	if len(conditions) == 0 {
		p.Children()[chIdx] = child
		return
	}
	
// 新建 LogicalSelection 连接在 child 上面,把 LogicalSelection 作为 p 的 child
	selection := LogicalSelection{Conditions: conditions}.Init(p.SCtx(), p.SelectBlockOffset())
	selection.SetChildren(child)
	p.Children()[chIdx] = selection
}

*LogicalSelection.PredicatePushDown

// PredicatePushDown implements LogicalPlan PredicatePushDown interface.
func (p *LogicalSelection) PredicatePushDown(predicates []expression.Expression) ([]expression.Expression, LogicalPlan) {
// 只保留 *expression.Constant,且为 true 的 predicate
// 要去除 ContainMutableConst,(prepare plan cache 那一套的)
	predicates = DeleteTrueExprs(p, predicates)
	p.Conditions = DeleteTrueExprs(p, p.Conditions)
	
	var child LogicalPlan
	var retConditions []expression.Expression
// having selection can't be pushed down, because it must above the aggregation.
	if p.buildByHaving {
		retConditions, child = p.children[0].PredicatePushDown(predicates)
		retConditions = append(retConditions, p.Conditions...)
	} else {
		canBePushDown, canNotBePushDown := splitSetGetVarFunc(p.Conditions)
// 把 p.Conditions 和上面传下来的 predicates 中能推的都推到孩子上
		retConditions, child = p.children[0].PredicatePushDown(append(canBePushDown, predicates...))
		retConditions = append(retConditions, canNotBePushDown...)
	}
	
	if len(retConditions) > 0 {
// 更新 p.Conditions 为 PropagateConstant 后的 retConditions
		p.Conditions = expression.PropagateConstant(p.ctx, retConditions)
// Return table dual when filter is constant false or null.
		dual := Conds2TableDual(p, p.Conditions)
		if dual != nil {
			return nil, dual
		}
// 推不下去的都留在 p 了
		return nil, p
	}
// 全都推下去了,p 没有用了,只需要 p.child
	return nil, child
}

*LogicalJoin.PredicatePushDown

LogicalProjection -> LogicalSelection (where) -> LogicalSelection (on) -> LogicalJoin
在这里插入图片描述

// PredicatePushDown implements LogicalPlan PredicatePushDown interface.
func (p *LogicalJoin) PredicatePushDown(predicates []expression.Expression) (ret []expression.Expression, retPlan LogicalPlan) {
// 首先会做一个简化,将左外连接和右外连接转化为内连接。
// 什么情况下外连接可以转内连接?左向外连接的结果集包括左表的所有行,而不仅仅是连接列所匹配的行。
// 如果左表的某行在右表中没有匹配的行,则在结果集右边补 NULL。做谓词下推时,如果我们知道接下来的
// 的谓词条件一定会把包含 NULL 的行全部都过滤掉,那么做外连接就没意义了,可以直接改写成内连接。
	simplifyOuterJoin(p, predicates)
	
	var equalCond []*expression.ScalarFunction
	var leftPushCond, rightPushCond, otherCond, leftCond, rightCond []expression.Expression
	switch p.JoinType {
	case LeftOuterJoin, LeftOuterSemiJoin, AntiLeftOuterSemiJoin:
		...
		
	case RightOuterJoin:
		...
		
	case SemiJoin, InnerJoin:
		tempCond := make([]expression.Expression, 0, len(p.LeftConditions)+len(p.RightConditions)+len(p.EqualConditions)+len(p.OtherConditions)+len(predicates))
		tempCond = append(tempCond, p.LeftConditions...)
		tempCond = append(tempCond, p.RightConditions...)
		tempCond = append(tempCond, expression.ScalarFuncs2Exprs(p.EqualConditions)...)
		tempCond = append(tempCond, p.OtherConditions...)
		tempCond = append(tempCond, predicates...)
// ???
		tempCond = expression.ExtractFiltersFromDNFs(p.ctx, tempCond)
		tempCond = expression.PropagateConstant(p.ctx, tempCond)
		// Return table dual when filter is constant false or null.
		dual := Conds2TableDual(p, tempCond)
		if dual != nil {
			return ret, dual
		}
// ExtractOnCondition divide conditions in CNF of join node into 4 groups.
// These conditions can be where conditions, join conditions, or collection of both.
// If deriveLeft/deriveRight is set, we would try to derive more conditions for left/right plan.
// 例如本例,有一个 t.a = s.a equalCond,
// 那么可以为 leftPushCond, rightPushCond 分别额外增加一个 not(isnull(myc_test.s.a)) 和 not(isnull(myc_test.t.a))
		equalCond, leftPushCond, rightPushCond, otherCond = p.extractOnCondition(tempCond, true, true)
// SemiJoin InnerJoin 不留 p.LeftConditions 和 p.RightConditions,全都推下去
		p.LeftConditions = nil
		p.RightConditions = nil
// equalCond 和 otherCond 留在 p
		p.EqualConditions = equalCond
		p.OtherConditions = otherCond
		leftCond = leftPushCond
		rightCond = rightPushCond
	case AntiSemiJoin:
		...

	}
	leftCond = expression.RemoveDupExprs(p.ctx, leftCond)
	rightCond = expression.RemoveDupExprs(p.ctx, rightCond)
	leftRet, lCh := p.children[0].PredicatePushDown(leftCond)
	rightRet, rCh := p.children[1].PredicatePushDown(rightCond)
 // push 不到孩子上的,生成一个 LogicalSelection 接在孩子头上
	addSelection(p, lCh, leftRet, 0)
	addSelection(p, rCh, rightRet, 1)
// ???
	p.updateEQCond()
// ???
	buildKeyInfo(p)
	return ret, p.self
}

*DataSource.PredicatePushDown

// PredicatePushDown implements LogicalPlan PredicatePushDown interface.
func (ds *DataSource) PredicatePushDown(predicates []expression.Expression) ([]expression.Expression, LogicalPlan) {
	predicates = expression.PropagateConstant(ds.ctx, predicates)
	predicates = DeleteTrueExprs(ds, predicates)
	ds.allConds = predicates
// pushedDownConds are the conditions that will be pushed down to coprocessor.
	ds.pushedDownConds, predicates = expression.PushDownExprs(ds.ctx.GetSessionVars().StmtCtx, predicates, ds.ctx.GetClient(), kv.UnSpecified)
// push 不到 coprocessor 上的,返回给上层
	return predicates, ds
}

*LogicalAggregation.PredicatePushDown

func (la *LogicalAggregation) PredicatePushDown(predicates []expression.Expression) (ret []expression.Expression, retPlan LogicalPlan) {
	var condsToPush []expression.Expression
// 在输入到 AggFuncs 前的 exprs,AggFuncs 的输出就是 la.Schema()
	exprsOriginal := make([]expression.Expression, 0, len(la.AggFuncs))
	for _, fun := range la.AggFuncs {
		exprsOriginal = append(exprsOriginal, fun.Args[0])
	}
	groupByColumns := expression.NewSchema(la.GetGroupByCols()...)
	for _, cond := range predicates {
		switch cond.(type) {
		case *expression.Constant:
			condsToPush = append(condsToPush, cond)
			ret = append(ret, cond)
		case *expression.ScalarFunction:
			extractedCols := expression.ExtractColumns(cond)
			ok := true
			for _, col := range extractedCols {
// 只能是 push 在 groupByColumns 中的 col
				if !groupByColumns.Contains(col) {
					ok = false
					break
				}
			}
			if ok {
// ??? 感觉像是把映射后的 col 转换成 映射前的 col
				newFunc := expression.ColumnSubstitute(cond, la.Schema(), exprsOriginal)
				condsToPush = append(condsToPush, newFunc)
			} else {
				ret = append(ret, cond)
			}
		default:
			ret = append(ret, cond)
		}
	}
	la.baseLogicalPlan.PredicatePushDown(condsToPush)
	return ret, la
}

joinReOrderSolver

type joinReOrderSolver struct {
}

func (s *joinReOrderSolver) optimize(ctx context.Context, p LogicalPlan) (LogicalPlan, error) {
	return s.optimizeRecursive(p.SCtx(), p)
}

*joinReOrderSolver.optimizeRecursive

// optimizeRecursive recursively collects join groups and applies join reorder algorithm for each group.
func (s *joinReOrderSolver) optimizeRecursive(ctx sessionctx.Context, p LogicalPlan) (LogicalPlan, error) {
	var err error
// 递归的 extracts all the join nodes connected with 
// 连续的 InnerJoins to construct a join group.
	curJoinGroup, eqEdges, otherConds := extractJoinGroup(p)
	
// 至少 p 是一个 InnerJoin 的 LogicalJoin
	if len(curJoinGroup) > 1 {
// curJoinGroup 中的所有成员是这一次 optimizeRecursive 调用中 p 需要 join reorder 的 joinNode
		for i := range curJoinGroup {
// curJoinGroup 中的每个成员都可以调用 optimizeRecursive 来进行各自的 join reorder 优化的
			curJoinGroup[i], err = s.optimizeRecursive(ctx, curJoinGroup[i])
			if err != nil {
				return nil, err
			}
		}
// 对 curJoinGroup 中的每个成员都调用 optimizeRecursive 进行各自的 join reorder 优化后
// 再对 curJoinGroup 中的所有成员进行作为 p 的 joinNode 的 join reorder 优化
		baseGroupSolver := &baseSingleGroupJoinOrderSolver{
			ctx:        ctx,
			otherConds: otherConds,
		}
		originalSchema := p.Schema()
//                									默认是 0
		if len(curJoinGroup) > ctx.GetSessionVars().TiDBOptJoinReorderThreshold {
			groupSolver := &joinReorderGreedySolver{
				baseSingleGroupJoinOrderSolver: baseGroupSolver,
				eqEdges:                        eqEdges,
			}
			p, err = groupSolver.solve(curJoinGroup)
		} else {
			dpSolver := &joinReorderDPSolver{
				baseSingleGroupJoinOrderSolver: baseGroupSolver,
			}
			dpSolver.newJoin = dpSolver.newJoinWithEdges
			p, err = dpSolver.solve(curJoinGroup, expression.ScalarFuncs2Exprs(eqEdges))
		}
		
		if err != nil {
			return nil, err
		}
		
// ??? join reorder 为啥还能把 p 的 Schema 改了
		schemaChanged := false
		if len(p.Schema().Columns) != len(originalSchema.Columns) {
			schemaChanged = true
		} else {
			for i, col := range p.Schema().Columns {
				if !col.Equal(nil, originalSchema.Columns[i]) {
					schemaChanged = true
					break
				}
			}
		}
// 如果 schema 被改了,那么就在上面接一个 LogicalProjection
		if schemaChanged {
			proj := LogicalProjection{
				Exprs: expression.Column2Exprs(originalSchema.Columns),
			}.Init(p.SCtx(), p.SelectBlockOffset())
			proj.SetSchema(originalSchema)
			proj.SetChildren(p)
			p = proj
		}
		
		return p, nil
	}

// p 不是一个 InnerJoin 的 LogicalJoin
// 对 p 的所有孩子均递归调用 s.optimizeRecursive 生成新孩子作为 p 的孩子
	newChildren := make([]LogicalPlan, 0, len(p.Children()))
	for _, child := range p.Children() {
		newChild, err := s.optimizeRecursive(ctx, child)
		if err != nil {
			return nil, err
		}
		newChildren = append(newChildren, newChild)
	}
	p.SetChildren(newChildren...)
	
	return p, nil
}
// extractJoinGroup extracts all the join nodes connected with 连续的
// InnerJoins to construct a join group. This join group is further used to
// construct a new join order based on a reorder algorithm.
//
// For example: "InnerJoin(InnerJoin(a, b), LeftJoin(c, d))"
// results in a join group {a, b, LeftJoin(c, d)}.
func extractJoinGroup(p LogicalPlan) (group []LogicalPlan, eqEdges []*expression.ScalarFunction, otherConds []expression.Expression) {
	join, isJoin := p.(*LogicalJoin)
// p 如果不是 LogicalJoin 
// 或 不是 InnerJoin 
// 或 用户使用 STRAIGHT_JOIN 语法来强制指定一种 Join 顺序
	if !isJoin || join.preferJoinType > uint(0) || join.JoinType != InnerJoin || join.StraightJoin {
		return []LogicalPlan{p}, nil, nil
	}

// 递归的 extracts all the join nodes connected with 连续的
// InnerJoins to construct a join group.
	lhsGroup, lhsEqualConds, lhsOtherConds := extractJoinGroup(join.children[0])
	rhsGroup, rhsEqualConds, rhsOtherConds := extractJoinGroup(join.children[1])

// InnerJoin 的 join.LeftConditions 和 join.RightConditions 一定为 nil
// 见上面的 PredicatePushDown
	group = append(group, lhsGroup...)
	group = append(group, rhsGroup...)
	eqEdges = append(eqEdges, join.EqualConditions...)
	eqEdges = append(eqEdges, lhsEqualConds...)
	eqEdges = append(eqEdges, rhsEqualConds...)
	otherConds = append(otherConds, join.OtherConditions...)
	otherConds = append(otherConds, lhsOtherConds...)
	otherConds = append(otherConds, rhsOtherConds...)
	return group, eqEdges, otherConds
}
joinReorderGreedySolver
type joinReorderGreedySolver struct {
	*baseSingleGroupJoinOrderSolver
	eqEdges []*expression.ScalarFunction
}

func (s *joinReorderGreedySolver) solve(joinNodePlans []LogicalPlan) (LogicalPlan, error) {
	for _, node := range joinNodePlans {
		_, err := node.recursiveDeriveStats(nil)
		if err != nil {
			return nil, err
		}
// 每一个 LogicalPlan 作为一个 jrNode 存在 s.curJoinGroup 中
		s.curJoinGroup = append(s.curJoinGroup, &jrNode{
			p:       node,
			cumCost: s.baseNodeCumCost(node),
		})
	}
// 按照 jrNode.cumCost 从小到大对 s.curJoinGroup 排序
	sort.SliceStable(s.curJoinGroup, func(i, j int) bool {
		return s.curJoinGroup[i].cumCost < s.curJoinGroup[j].cumCost
	})

// cartesianGroup 中的每一个 LogicalPlan 和其他 LogicalPlan 无关联,
// 即它们之间,不存在 joinReorderGreedySolver.eqEdges
	var cartesianGroup []LogicalPlan
	for len(s.curJoinGroup) > 0 {
		newNode, err := s.constructConnectedJoinTree()
		if err != nil {
			return nil, err
		}
		cartesianGroup = append(cartesianGroup, newNode.p)
	}

	return s.makeBushyJoin(cartesianGroup), nil
}
// baseNodeCumCost calculate the cumulative cost of the node in the join group.
func (s *baseSingleGroupJoinOrderSolver) baseNodeCumCost(groupNode LogicalPlan) float64 {
	cost := groupNode.statsInfo().RowCount
	for _, child := range groupNode.Children() {
		cost += s.baseNodeCumCost(child)
	}
	return cost
}

https://docs.pingcap.com/zh/tidb/dev/join-reorder

func (s *joinReorderGreedySolver) constructConnectedJoinTree() (*jrNode, error) {
// 第一个 jrNode.cumCost 是最小的,因为前面排过序了
// 把它作为 curJoinTree
	curJoinTree := s.curJoinGroup[0]
	s.curJoinGroup = s.curJoinGroup[1:]
	for {
		bestCost := math.MaxFloat64
		bestIdx := -1
		var finalRemainOthers []expression.Expression
		var bestJoin LogicalPlan
		for i, node := range s.curJoinGroup {
// 如果 node, curJoinTree 之间存在至少一个 s.eqEdges
// 那说明这俩可以组成一个新的 LogicalJoin 为 newJoin
			newJoin, remainOthers := s.checkConnectionAndMakeJoin(curJoinTree.p, node.p)
			if newJoin == nil {
				continue
			}
			_, err := newJoin.recursiveDeriveStats(nil)
			if err != nil {
				return nil, err
			}
			curCost := s.calcJoinCumCost(newJoin, curJoinTree, node)
// 如果 newJoin 是迄今为止最优,那么 bestJoin = newJoin
			if bestCost > curCost {
				bestCost = curCost
				bestJoin = newJoin
				bestIdx = i
				finalRemainOthers = remainOthers
			}
		}
		// If we could find more join node, meaning that the sub connected graph have been totally explored.
		if bestJoin == nil {
			break
		}
// curJoinTree 更新为,新产生的,合并过后的 bestJoin
		curJoinTree = &jrNode{
			p:       bestJoin,
			cumCost: bestCost,
		}
// 从 s.curJoinGroup 中删除掉被合并的 node
		s.curJoinGroup = append(s.curJoinGroup[:bestIdx], s.curJoinGroup[bestIdx+1:]...)
		s.otherConds = finalRemainOthers
	}
	return curJoinTree, nil
}

func (s *joinReorderGreedySolver) checkConnectionAndMakeJoin(leftNode, rightNode LogicalPlan) (LogicalPlan, []expression.Expression) {
	var usedEdges []*expression.ScalarFunction
	remainOtherConds := make([]expression.Expression, len(s.otherConds))
	copy(remainOtherConds, s.otherConds)
// 如果 leftNode, rightNode 之间存在至少一个 s.eqEdges
// 那说明这俩可以组成一个新的 LogicalJoin
	for _, edge := range s.eqEdges {
		lCol := edge.GetArgs()[0].(*expression.Column)
		rCol := edge.GetArgs()[1].(*expression.Column)
		if leftNode.Schema().Contains(lCol) && rightNode.Schema().Contains(rCol) {
			usedEdges = append(usedEdges, edge)
		} else if rightNode.Schema().Contains(lCol) && leftNode.Schema().Contains(rCol) {
			newSf := expression.NewFunctionInternal(s.ctx, ast.EQ, edge.GetType(), rCol, lCol).(*expression.ScalarFunction)
			usedEdges = append(usedEdges, newSf)
		}
	}
	if len(usedEdges) == 0 {
		return nil, nil
	}
	
	var otherConds []expression.Expression
	mergedSchema := expression.MergeSchema(leftNode.Schema(), rightNode.Schema())
	
// 从 remainOtherConds 中筛选出属于 leftNode, rightNode 的 otherConds
	remainOtherConds, otherConds = expression.FilterOutInPlace(remainOtherConds, func(expr expression.Expression) bool {
		return expression.ExprFromSchema(expr, mergedSchema)
	})
	return s.newJoinWithEdges(leftNode, rightNode, usedEdges, otherConds), remainOtherConds
}

// 二和一,并设置 Logical.EqualConditions 和 Logical.OtherConditions
func (s *baseSingleGroupJoinOrderSolver) newJoinWithEdges(lChild, rChild LogicalPlan, eqEdges []*expression.ScalarFunction, otherConds []expression.Expression) LogicalPlan {
	newJoin := s.newCartesianJoin(lChild, rChild)
	newJoin.EqualConditions = eqEdges
	newJoin.OtherConditions = otherConds
	return newJoin
}
// makeBushyJoin build bushy tree for the nodes which have no equal condition to connect them.
// 不断将相互之间不存在 eqConditions 的两个 LogicalPlan 二合一成为一个 LogicalJoin
func (s *baseSingleGroupJoinOrderSolver) makeBushyJoin(cartesianJoinGroup []LogicalPlan) LogicalPlan {
	resultJoinGroup := make([]LogicalPlan, 0, (len(cartesianJoinGroup)+1)/2)
	for len(cartesianJoinGroup) > 1 {
		resultJoinGroup = resultJoinGroup[:0]
		for i := 0; i < len(cartesianJoinGroup); i += 2 {
			if i+1 == len(cartesianJoinGroup) {
				resultJoinGroup = append(resultJoinGroup, cartesianJoinGroup[i])
				break
			}
// 二和一成为 LogicalJoin
			newJoin := s.newCartesianJoin(cartesianJoinGroup[i], cartesianJoinGroup[i+1])
// 设置 newJoin.OtherConditions,
// 无需设置 newJoin.EqualConditions,
// 因为 cartesianJoinGroup[i], cartesianJoinGroup[i+1] 之间不存在 eqConditions
			for i := len(s.otherConds) - 1; i >= 0; i-- {
				cols := expression.ExtractColumns(s.otherConds[i])
				if newJoin.schema.ColumnsIndices(cols) != nil {
					newJoin.OtherConditions = append(newJoin.OtherConditions, s.otherConds[i])
					s.otherConds = append(s.otherConds[:i], s.otherConds[i+1:]...)
				}
			}
			
			resultJoinGroup = append(resultJoinGroup, newJoin)
		}
		cartesianJoinGroup, resultJoinGroup = resultJoinGroup, cartesianJoinGroup
	}
	return cartesianJoinGroup[0]
}
func (s *baseSingleGroupJoinOrderSolver) newCartesianJoin(lChild, rChild LogicalPlan) *LogicalJoin {
// ??? BlockOffset 到底是干啥的
// 当查询语句是包含多层嵌套子查询的复杂语句时,
// SelectBlockOffset 代表这个 LogicalPlan 是属于哪个 Select语句的
	offset := lChild.SelectBlockOffset()
	if offset != rChild.SelectBlockOffset() {
		offset = -1
	}
	
	join := LogicalJoin{
		JoinType:  InnerJoin,
		reordered: true,
	}.Init(s.ctx, offset)
	join.SetSchema(expression.MergeSchema(lChild.Schema(), rChild.Schema()))
	join.SetChildren(lChild, rChild)
	return join
}
joinReorderDPSolver
type joinReorderDPSolver struct {
	*baseSingleGroupJoinOrderSolver
	newJoin func(lChild, rChild LogicalPlan, eqConds []*expression.ScalarFunction, otherConds []expression.Expression) LogicalPlan
}

func (s *joinReorderDPSolver) solve(joinGroup []LogicalPlan, eqConds []expression.Expression) (LogicalPlan, error) {
	for _, node := range joinGroup {
		_, err := node.recursiveDeriveStats(nil)
		if err != nil {
			return nil, err
		}
		s.curJoinGroup = append(s.curJoinGroup, &jrNode{
			p:       node,
			cumCost: s.baseNodeCumCost(node),
		})
	}
// adjacents[i] 代表的是所有和 i 存在 eqCond 的 LogicalPlan
	adjacents := make([][]int, len(s.curJoinGroup))
// totalEqEdges 代表的是所有 eqConds
	totalEqEdges := make([]joinGroupEqEdge, 0, len(eqConds))
	// Build Graph for join group
	for _, cond := range eqConds {
		sf := cond.(*expression.ScalarFunction)
		lCol := sf.GetArgs()[0].(*expression.Column)
		rCol := sf.GetArgs()[1].(*expression.Column)
		lIdx, err := findNodeIndexInGroup(joinGroup, lCol)
		if err != nil {
			return nil, err
		}
		rIdx, err := findNodeIndexInGroup(joinGroup, rCol)
		if err != nil {
			return nil, err
		}
// lIdx 和 rIdx 之间存在 eqcond
		totalEqEdges = append(totalEqEdges, joinGroupEqEdge{
			nodeIDs: []int{node1, node2},
			edge:    edgeContent,
		})
		adjacents[node1] = append(adjacents[node1], node2)
		adjacents[node2] = append(adjacents[node2], node1)
	}
// totalNonEqEdges 代表的是所有 otherConds
	totalNonEqEdges := make([]joinGroupNonEqEdge, 0, len(s.otherConds))
	for _, cond := range s.otherConds {
		cols := expression.ExtractColumns(cond)
		mask := uint(0)
		ids := make([]int, 0, len(cols))
		for _, col := range cols {
			idx, err := findNodeIndexInGroup(joinGroup, col)
			if err != nil {
				return nil, err
			}
			ids = append(ids, idx)
// 用一个 mask 来表示这个 cond 所涉及到的所有 col 所属的 LogicalPlan 的 index
//                  把 1 左移 uint(idx) 位
			mask |= 1 << uint(idx)
		}
		totalNonEqEdges = append(totalNonEqEdges, joinGroupNonEqEdge{
			nodeIDs:    ids,
			nodeIDMask: mask,
			expr:       cond,
		})
	}
	
	visited := make([]bool, len(joinGroup))
	nodeID2VisitID := make([]int, len(joinGroup))
	var joins []LogicalPlan
	// BFS the tree.
	for i := 0; i < len(joinGroup); i++ {
		if visited[i] {
			continue
		}
// 找到和 i 存在直接或间接 eqcond 的所有 node
		visitID2NodeID := s.bfsGraph(i, visited, adjacents, nodeID2VisitID)
		nodeIDMask := uint(0)
		for _, nodeID := range visitID2NodeID {
			nodeIDMask |= 1 << uint(nodeID)
		}
		var subNonEqEdges []joinGroupNonEqEdge
		for i := len(totalNonEqEdges) - 1; i >= 0; i-- {
			// If this edge is not the subset of the current sub graph.
// totalNonEqEdges[i] 中所涉及到的部分或所有 node,不存在于 visitID2NodeID 中
// 忽略这个不属于这个子图的 NonEqEdge
			if totalNonEqEdges[i].nodeIDMask&nodeIDMask != totalNonEqEdges[i].nodeIDMask {
				continue
			}
// 重新设置一下 totalNonEqEdges[i].nodeIDMask
			newMask := uint(0)
			for _, nodeID := range totalNonEqEdges[i].nodeIDs {
				newMask |= 1 << uint(nodeID2VisitID[nodeID])
			}
			totalNonEqEdges[i].nodeIDMask = newMask
// 把这一项加入到 subNonEqEdges 中去
			subNonEqEdges = append(subNonEqEdges, totalNonEqEdges[i])
// 从 totalNonEqEdges 删除这一项
			totalNonEqEdges = append(totalNonEqEdges[:i], totalNonEqEdges[i+1:]...)
		}
		// Do DP on each sub graph.
		join, err := s.dpGraph(visitID2NodeID, nodeID2VisitID, joinGroup, totalEqEdges, subNonEqEdges)
		if err != nil {
			return nil, err
		}
// joins 中的每一项和剩余其他项都没有关联,不存在 eqconds
		joins = append(joins, join)
	}
	remainedOtherConds := make([]expression.Expression, 0, len(totalNonEqEdges))
	for _, edge := range totalNonEqEdges {
		remainedOtherConds = append(remainedOtherConds, edge.expr)
	}
	// Build bushy tree for cartesian joins.
	return s.makeBushyJoin(joins, remainedOtherConds), nil
}
// bfsGraph bfs a sub graph starting at startPos. And relabel its label for future use.
func (s *joinReorderDPSolver) bfsGraph(startNode int, visited []bool, adjacents [][]int, nodeID2VistID []int) []int {
	queue := []int{startNode}
	visited[startNode] = true
	var visitID2NodeID []int
	for len(queue) > 0 {
		curNodeID := queue[0]
		queue = queue[1:]
// nodeID2VistID 记录了 NodeID 在 visitID2NodeID 中的 index
		nodeID2VistID[curNodeID] = len(visitID2NodeID)
// visitID2NodeID 记录了所有和 startNode 直接相关或者间接相关的 Node
		visitID2NodeID = append(visitID2NodeID, curNodeID)
		for _, adjNodeID := range adjacents[curNodeID] {
			if visited[adjNodeID] {
				continue
			}
			queue = append(queue, adjNodeID)
			visited[adjNodeID] = true
		}
	}
	return visitID2NodeID
}
// dpGraph is the core part of this algorithm.
// It implements the traditional join reorder algorithm: DP by subset using the following formula:
//   bestPlan[S:set of node] = the best one among Join(bestPlan[S1:subset of S], bestPlan[S2: S/S1])
func (s *joinReorderDPSolver) dpGraph(visitID2NodeID, nodeID2VisitID []int, joinGroup []LogicalPlan,
	totalEqEdges []joinGroupEqEdge, totalNonEqEdges []joinGroupNonEqEdge) (LogicalPlan, error) {
	nodeCnt := uint(len(visitID2NodeID))
// 1<<nodeCnt 是 1 左移 nodeCnt 位,
// 代表 visitID2NodeID 有 2 的 nodeCnt 次方种子集
// 把每种子集的 bestplan 求出来,就解出来了
	bestPlan := make([]*jrNode, 1<<nodeCnt)
	// bestPlan[s] is nil can be treated as bestCost[s] = +inf.
// 这里先把只有一个 node 的子集的 bestplan 设置一下
// 只有一个 node 的子集的 bestplan 就是这个 node 自己嘛
	for i := uint(0); i < nodeCnt; i++ {
		bestPlan[1<<i] = s.curJoinGroup[visitID2NodeID[i]]
	}
// Enumerate the nodeBitmap from small to big, make sure that S1 must be enumerated before S2 if S1 belongs to S2.
// 每次循环,nodeBitmap 都代表一个集合,是 visitID2NodeID 的一个子集
	for nodeBitmap := uint(1); nodeBitmap < (1 << nodeCnt); nodeBitmap++ {
// 只有一个 node 的子集的 bestplan 已经被设置过了
		if bits.OnesCount(nodeBitmap) == 1 {
			continue
		}
		// This loop can iterate all its subset.
// 遍历 nodeBitmap 所代表集合的所有子集 sub
// remain 为 sub 的补集,remain + sub = nodeBitmap
		for sub := (nodeBitmap - 1) & nodeBitmap; sub > 0; sub = (sub - 1) & nodeBitmap {
			remain := nodeBitmap ^ sub
			if sub > remain {
				continue
			}
			// If this subset is not connected skip it.
			if bestPlan[sub] == nil || bestPlan[remain] == nil {
				continue
			}
			// Get the edge connecting the two parts.
// 检查 sub 所代表的 nodeBitmap 的子集和,与 remain 所代表的 nodeBitmap 的子集和
// 之间是否存在 eqconds
			usedEdges, otherConds := s.nodesAreConnected(sub, remain, nodeID2VisitID, totalEqEdges, totalNonEqEdges)
			// Here we only check equal condition currently.
			if len(usedEdges) == 0 {
				continue
			}
// 将 bestPlan[sub].p 和 bestPlan[remain].p 做一个 join
			join, err := s.newJoinWithEdge(bestPlan[sub].p, bestPlan[remain].p, usedEdges, otherConds)
			if err != nil {
				return nil, err
			}
// 计算出 cost
			curCost := s.calcJoinCumCost(join, bestPlan[sub], bestPlan[remain])
// 更新 bestPlan[nodeBitmap]
			if bestPlan[nodeBitmap] == nil {
				bestPlan[nodeBitmap] = &jrNode{
					p:       join,
					cumCost: curCost,
				}
			} else if bestPlan[nodeBitmap].cumCost > curCost {
				bestPlan[nodeBitmap].p = join
				bestPlan[nodeBitmap].cumCost = curCost
			}
		}
	}
	return bestPlan[(1<<nodeCnt)-1].p, nil
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值