// Join represents table join.
type Join struct {
node
resultSetNode
// Left table can be TableSource or JoinNode.
Left ResultSetNode
// Right table can be TableSource or JoinNode or nil.
Right ResultSetNode
// Tp represents join type.
Tp JoinType
// On represents join on condition.
On *OnCondition
// Using represents join using clause.
Using []*ColumnName
// NaturalJoin represents join is natural join
NaturalJoin bool
}
func (b *planBuilder) buildJoin(join *ast.Join) LogicalPlan {
b.optFlag = b.optFlag | flagPredicatePushDown
leftPlan := b.buildResultSetNode(join.Left)
rightPlan := b.buildResultSetNode(join.Right)
newSchema := expression.MergeSchema(leftPlan.Schema(), rightPlan.Schema())
joinPlan := LogicalJoin{}.init(b.ctx)
joinPlan.SetChildren(leftPlan, rightPlan)
joinPlan.SetSchema(newSchema)
// Merge sub join's redundantSchema into this join plan. When handle query like
// select t2.a from (t1 join t2 using (a)) join t3 using (a);
// we can simply search in the top level join plan to find redundant column.
var lRedundant, rRedundant *expression.Schema
if left, ok := leftPlan.(*LogicalJoin); ok && left.redundantSchema != nil {
lRedundant = left.redundantSchema
}
if right, ok := rightPlan.(*LogicalJoin); ok && right.redundantSchema != nil {
rRedundant = right.redundantSchema
}
joinPlan.redundantSchema = expression.MergeSchema(lRedundant, rRedundant)
b.curClause = onClause
onExpr, newPlan, err := b.rewrite(join.On.Expr, joinPlan, nil, false)
onCondition := expression.SplitCNFItems(onExpr)
joinPlan.attachOnConds(onCondition)
joinPlan.JoinType = InnerJoin
return joinPlan
}