Skip to content
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

Fix dual merging in outer join queries #15959

Merged
merged 6 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions go/vt/sqlparser/ast_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -1938,6 +1938,26 @@ func (joinType JoinType) IsCommutative() bool {
}
}

// IsLeft returns whether the join type is left join type or not.
func (joinType JoinType) IsLeft() bool {
switch joinType {
case LeftJoinType, NaturalLeftJoinType:
return true
default:
return false
}
}

// IsRight returns whether the join type is right join type or not.
func (joinType JoinType) IsRight() bool {
switch joinType {
case RightJoinType, NaturalRightJoinType:
return true
default:
return false
}
}

// IsInner returns whether the join type is an inner join or not.
func (joinType JoinType) IsInner() bool {
switch joinType {
Expand Down
19 changes: 10 additions & 9 deletions go/vt/vtgate/planbuilder/operators/join_merging.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,24 @@ import (
// mergeJoinInputs checks whether two operators can be merged into a single one.
// If they can be merged, a new operator with the merged routing is returned
// If they cannot be merged, nil is returned.
func mergeJoinInputs(ctx *plancontext.PlanningContext, lhs, rhs Operator, joinPredicates []sqlparser.Expr, m merger) *Route {
func mergeJoinInputs(ctx *plancontext.PlanningContext, lhs, rhs Operator, joinPredicates []sqlparser.Expr, m *joinMerger) *Route {
lhsRoute, rhsRoute, routingA, routingB, a, b, sameKeyspace := prepareInputRoutes(lhs, rhs)
if lhsRoute == nil {
return nil
}

switch {
// if either side is a dual query, we can always merge them together
case a == dual:
// If a dual is on the left side and it is a left join, then we can only merge if the right side is a single sharded routing.
if m.joinType.IsLeft() && !routingB.OpCode().IsSingleShard() {
return nil
}
return m.merge(ctx, lhsRoute, rhsRoute, routingB)
case b == dual:
// If a dual is on the right side and it is a right join, then we can only merge if the left side is a single sharded routing.
if m.joinType.IsRight() && !routingA.OpCode().IsSingleShard() {
return nil
}
return m.merge(ctx, lhsRoute, rhsRoute, routingA)

// an unsharded/reference route can be merged with anything going to that keyspace
Expand Down Expand Up @@ -74,12 +81,6 @@ func prepareInputRoutes(lhs Operator, rhs Operator) (*Route, *Route, Routing, Ro
lhsRoute, rhsRoute, routingA, routingB, sameKeyspace := getRoutesOrAlternates(lhsRoute, rhsRoute)

a, b := getRoutingType(routingA), getRoutingType(routingB)
if getTypeName(routingA) < getTypeName(routingB) {
// while deciding if two routes can be merged, the LHS/RHS order of the routes is not important.
// for the actual merging, we still need to remember which side was inner and which was outer for subqueries
a, b = b, a
routingA, routingB = routingB, routingA
}

return lhsRoute, rhsRoute, routingA, routingB, a, b, sameKeyspace
}
Expand Down Expand Up @@ -178,7 +179,7 @@ func getRoutingType(r Routing) routingType {
panic(fmt.Sprintf("switch should be exhaustive, got %T", r))
}

func newJoinMerge(predicates []sqlparser.Expr, joinType sqlparser.JoinType) merger {
func newJoinMerge(predicates []sqlparser.Expr, joinType sqlparser.JoinType) *joinMerger {
return &joinMerger{
predicates: predicates,
joinType: joinType,
Expand Down
115 changes: 115 additions & 0 deletions go/vt/vtgate/planbuilder/testdata/select_cases.json
Original file line number Diff line number Diff line change
Expand Up @@ -2043,6 +2043,121 @@
]
}
},
{
GuptaManan100 marked this conversation as resolved.
Show resolved Hide resolved
"comment": "left join with a dual table on left",
"query": "select t.title, user.col from (select 'hello' as title) as t left join user on user.id=1",
"plan": {
"QueryType": "SELECT",
"Original": "select t.title, user.col from (select 'hello' as title) as t left join user on user.id=1",
"Instructions": {
"OperatorType": "Join",
"Variant": "LeftJoin",
"JoinColumnIndexes": "L:0,R:0",
"TableName": "dual_`user`",
"Inputs": [
{
"OperatorType": "Route",
"Variant": "Reference",
"Keyspace": {
"Name": "main",
"Sharded": false
},
"FieldQuery": "select t.title from (select 'hello' as title from dual where 1 != 1) as t where 1 != 1",
"Query": "select t.title from (select 'hello' as title from dual) as t",
"Table": "dual"
},
{
"OperatorType": "Route",
"Variant": "EqualUnique",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"FieldQuery": "select `user`.col from `user` where 1 != 1",
"Query": "select `user`.col from `user` where `user`.id = 1",
"Table": "`user`",
"Values": [
"1"
],
"Vindex": "user_index"
}
]
GuptaManan100 marked this conversation as resolved.
Show resolved Hide resolved
},
"TablesUsed": [
"main.dual",
"user.user"
]
}
},
{
"comment": "right join with a dual table on left",
"query": "select t.title, user.col from (select 'hello' as title) as t right join user on user.id=1",
"plan": {
"QueryType": "SELECT",
"Original": "select t.title, user.col from (select 'hello' as title) as t right join user on user.id=1",
"Instructions": {
"OperatorType": "Route",
"Variant": "Scatter",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"FieldQuery": "select t.title, `user`.col from `user` left join (select 'hello' as title from dual where 1 != 1) as t on `user`.id = 1 where 1 != 1",
"Query": "select t.title, `user`.col from `user` left join (select 'hello' as title from dual) as t on `user`.id = 1",
"Table": "`user`, dual"
},
"TablesUsed": [
"main.dual",
"user.user"
]
}
},
{
"comment": "right join with a dual table on right",
"query": "select t.title, user.col from user right join (select 'hello' as title) as t on user.id=1",
"plan": {
"QueryType": "SELECT",
"Original": "select t.title, user.col from user right join (select 'hello' as title) as t on user.id=1",
"Instructions": {
"OperatorType": "Join",
"Variant": "LeftJoin",
"JoinColumnIndexes": "L:0,R:0",
"TableName": "dual_`user`",
"Inputs": [
{
"OperatorType": "Route",
"Variant": "Reference",
"Keyspace": {
"Name": "main",
"Sharded": false
},
"FieldQuery": "select t.title from (select 'hello' as title from dual where 1 != 1) as t where 1 != 1",
"Query": "select t.title from (select 'hello' as title from dual) as t",
"Table": "dual"
},
{
"OperatorType": "Route",
"Variant": "EqualUnique",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"FieldQuery": "select `user`.col from `user` where 1 != 1",
"Query": "select `user`.col from `user` where `user`.id = 1",
"Table": "`user`",
"Values": [
"1"
],
"Vindex": "user_index"
}
]
},
"TablesUsed": [
"main.dual",
"user.user"
]
}
},
{
"comment": "Union after into outfile is incorrect",
"query": "select id from user into outfile 'out_file_name' union all select id from music",
Expand Down
Loading