-
Notifications
You must be signed in to change notification settings - Fork 5.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
*: support using clause in join statement. #3372
Changes from 4 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ package plan | |
|
||
import ( | ||
"fmt" | ||
"sort" | ||
|
||
"github.com/juju/errors" | ||
"github.com/pingcap/tidb/ast" | ||
|
@@ -227,6 +228,15 @@ func (b *planBuilder) buildJoin(join *ast.Join) LogicalPlan { | |
addChild(joinPlan, rightPlan) | ||
joinPlan.SetSchema(newSchema) | ||
|
||
var lCoalesced, rCoalesced *expression.Schema | ||
if left, ok := leftPlan.(*LogicalJoin); ok && left.coalescedSchema != nil { | ||
lCoalesced = left.coalescedSchema | ||
} | ||
if right, ok := rightPlan.(*LogicalJoin); ok && right.coalescedSchema != nil { | ||
rCoalesced = right.coalescedSchema | ||
} | ||
joinPlan.coalescedSchema = expression.MergeSchema(lCoalesced, rCoalesced) | ||
|
||
if b.TableHints() != nil { | ||
joinPlan.preferMergeJoin = b.TableHints().ifPreferMergeJoin(leftAlias, rightAlias) | ||
if b.TableHints().ifPreferINLJ(leftAlias) { | ||
|
@@ -240,7 +250,12 @@ func (b *planBuilder) buildJoin(join *ast.Join) LogicalPlan { | |
} | ||
} | ||
|
||
if join.On != nil { | ||
if join.Using != nil { | ||
if err := b.buildUsingClause(joinPlan, leftPlan, rightPlan, join); err != nil { | ||
b.err = err | ||
return nil | ||
} | ||
} else if join.On != nil { | ||
onExpr, _, err := b.rewrite(join.On.Expr, joinPlan, nil, false) | ||
if err != nil { | ||
b.err = err | ||
|
@@ -266,6 +281,71 @@ func (b *planBuilder) buildJoin(join *ast.Join) LogicalPlan { | |
return joinPlan | ||
} | ||
|
||
func (b *planBuilder) buildUsingClause(p *LogicalJoin, leftPlan, rightPlan LogicalPlan, join *ast.Join) error { | ||
lsc := leftPlan.Schema().Clone() | ||
rsc := rightPlan.Schema().Clone() | ||
|
||
schemaCols := make([]*expression.Column, 0, len(lsc.Columns)+len(rsc.Columns)-len(join.Using)) | ||
coalescedCols := make([]*expression.Column, 0, len(join.Using)) | ||
conds := make([]*expression.ScalarFunction, 0, len(join.Using)) | ||
|
||
coalesced := make(map[string]bool, len(join.Using)) | ||
for _, col := range join.Using { | ||
var ( | ||
err error | ||
lc, rc *expression.Column | ||
cond expression.Expression | ||
) | ||
|
||
if lc, err = lsc.FindColumn(col); err != nil { | ||
return errors.Trace(err) | ||
} | ||
if rc, err = rsc.FindColumn(col); err != nil { | ||
return errors.Trace(err) | ||
} | ||
coalesced[col.Name.L] = true | ||
if lc == nil || rc == nil { | ||
return ErrUnknownColumn.GenByArgs(col.Name, "from clause") | ||
} | ||
|
||
if cond, err = expression.NewFunction(b.ctx, ast.EQ, types.NewFieldType(mysql.TypeTiny), lc, rc); err != nil { | ||
return errors.Trace(err) | ||
} | ||
conds = append(conds, cond.(*expression.ScalarFunction)) | ||
if join.Tp == ast.RightJoin { | ||
schemaCols = append(schemaCols, rc) | ||
coalescedCols = append(coalescedCols, lc) | ||
} else { | ||
schemaCols = append(schemaCols, lc) | ||
coalescedCols = append(coalescedCols, rc) | ||
} | ||
} | ||
|
||
sort.Slice(schemaCols, func(i, j int) bool { | ||
return schemaCols[i].Position < schemaCols[j].Position | ||
}) | ||
|
||
if join.Tp == ast.RightJoin { | ||
lsc, rsc = rsc, lsc | ||
} | ||
for _, col := range lsc.Columns { | ||
if !coalesced[col.ColName.L] { | ||
schemaCols = append(schemaCols, col) | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. According to MySQL
And the So I first found the columns which in both table, then sort them based on their position in source table, then switch the |
||
for _, col := range rsc.Columns { | ||
if !coalesced[col.ColName.L] { | ||
schemaCols = append(schemaCols, col) | ||
} | ||
} | ||
|
||
p.SetSchema(expression.NewSchema(schemaCols...)) | ||
p.EqualConditions = append(conds, p.EqualConditions...) | ||
p.coalescedSchema = expression.MergeSchema(p.coalescedSchema, expression.NewSchema(coalescedCols...)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
select * from (t1 join t2 using (a)) join (t3 join t4 using (a)) on (t2.a = t4.a and t1.a = t3.a) L231-L238 merge the sub join's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need to copy sub join's coalescedSchema to outer join?
The outer join's coalesedSchema should be If t3 doesn't have column
should return error. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But I see we can access coalesced columns explicit in MySQL: select t2.a from (t1 join t2 using (a)) join t3 using (a);
select * from (t1 join t2 using (a) join t4 using (a)) join t3 on (t1.a = t2.a);
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bobotu There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bobotu There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I'll change it's name and specify the comment. |
||
|
||
return nil | ||
} | ||
|
||
func (b *planBuilder) buildSelection(p LogicalPlan, where ast.ExprNode, AggMapper map[*ast.AggregateFuncExpr]int) LogicalPlan { | ||
b.optFlag = b.optFlag | flagPredicatePushDown | ||
conditions := splitWhere(where) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just
errors.Trace(err)
?And it's impossible for coalesced schema to return an ambiguous error.