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

Rewrite USING to ON condition for joins #13931

Merged
merged 10 commits into from
Sep 8, 2023
8 changes: 8 additions & 0 deletions go/test/endtoend/vtgate/queries/misc/misc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,3 +296,11 @@ func TestBuggyOuterJoin(t *testing.T) {
mcmp.Exec("insert into t1(id1, id2) values (1,2), (42,5), (5, 42)")
mcmp.Exec("select t1.id1, t2.id1 from t1 left join t1 as t2 on t2.id1 = t2.id2")
}

func TestLeftJoinUsingUnsharded(t *testing.T) {
mcmp, closer := start(t)
defer closer()

utils.Exec(t, mcmp.VtConn, "insert into uks.unsharded(id1) values (1),(2),(3),(4),(5)")
utils.Exec(t, mcmp.VtConn, "select * from uks.unsharded as A left join uks.unsharded as B using(id1)")
}
22 changes: 22 additions & 0 deletions go/vt/vtgate/planbuilder/testdata/from_cases.json
Original file line number Diff line number Diff line change
Expand Up @@ -4017,5 +4017,27 @@
"zlookup_unique.t1"
]
}
},
{
"comment": "left join with using has to be transformed into inner join with on condition",
"query": "SELECT * FROM unsharded_authoritative as A LEFT JOIN unsharded_authoritative as B USING(col1)",
"plan": {
"QueryType": "SELECT",
"Original": "SELECT * FROM unsharded_authoritative as A LEFT JOIN unsharded_authoritative as B USING(col1)",
"Instructions": {
"OperatorType": "Route",
"Variant": "Unsharded",
"Keyspace": {
"Name": "main",
"Sharded": false
},
"FieldQuery": "select A.col1 as col1, A.col2 as col2, B.col2 as col2 from unsharded_authoritative as A left join unsharded_authoritative as B on A.col1 = B.col1 where 1 != 1",
"Query": "select A.col1 as col1, A.col2 as col2, B.col2 as col2 from unsharded_authoritative as A left join unsharded_authoritative as B on A.col1 = B.col1",
"Table": "unsharded_authoritative"
},
"TablesUsed": [
"main.unsharded_authoritative"
]
}
}
]
18 changes: 17 additions & 1 deletion go/vt/vtgate/semantics/binder.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,23 @@ func (b *binder) up(cursor *sqlparser.Cursor) error {
currScope.joinUsing[ident.Lowered()] = deps.direct
}
if len(node.Using) > 0 {
err := rewriteJoinUsing(currScope, node.Using, b.org)
err := rewriteJoinUsing(currScope, node, b.org)
if err != nil {
return err
}
err = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
column, isColumn := node.(*sqlparser.ColName)
if !isColumn {
return true, nil
}
deps, err := b.resolveColumn(column, currScope, false)
if err != nil {
return false, err
}
b.recursive[column] = deps.recursive
b.direct[column] = deps.direct
return true, nil
}, node.On)
harshit-gangal marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return err
}
Expand Down
29 changes: 4 additions & 25 deletions go/vt/vtgate/semantics/early_rewriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,34 +344,13 @@ func rewriteOrFalse(orExpr sqlparser.OrExpr) sqlparser.Expr {
//
// This function returns an error if it encounters a non-authoritative table or
// if it cannot find a SELECT statement to add the WHERE predicate to.
func rewriteJoinUsing(
current *scope,
using sqlparser.Columns,
org originable,
) error {
predicates, err := buildJoinPredicates(current, using, org)
func rewriteJoinUsing(current *scope, cond *sqlparser.JoinCondition, org originable) error {
predicates, err := buildJoinPredicates(current, cond.Using, org)
if err != nil {
return err
}
// now, we go up the scope until we find a SELECT
// with a where clause we can add this predicate to
for current != nil {
sel, found := current.stmt.(*sqlparser.Select)
if !found {
current = current.parent
continue
}
if sel.Where != nil {
predicates = append(predicates, sel.Where.Expr)
sel.Where = nil
}
sel.Where = &sqlparser.Where{
Type: sqlparser.WhereClause,
Expr: sqlparser.AndExpressions(predicates...),
}
return nil
}
return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "did not find WHERE clause")
cond.On = sqlparser.AndExpressions(predicates...)
frouioui marked this conversation as resolved.
Show resolved Hide resolved
return nil
}

// buildJoinPredicates constructs the join predicates for a given set of USING columns.
Expand Down
14 changes: 7 additions & 7 deletions go/vt/vtgate/semantics/early_rewriter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,24 +146,24 @@ func TestExpandStar(t *testing.T) {
expSQL: "select t1.a as a, t1.b as b, t1.c as c, t2.c1 as c1, t2.c2 as c2 from t1 join t2 on t1.a = t2.c1",
}, {
sql: "select * from t2 join t4 using (c1)",
expSQL: "select t2.c1 as c1, t2.c2 as c2, t4.c4 as c4 from t2 join t4 where t2.c1 = t4.c1",
expSQL: "select t2.c1 as c1, t2.c2 as c2, t4.c4 as c4 from t2 join t4 on t2.c1 = t4.c1",
expanded: "main.t2.c1, main.t2.c2, main.t4.c4",
}, {
sql: "select * from t2 join t4 using (c1) join t2 as X using (c1)",
expSQL: "select t2.c1 as c1, t2.c2 as c2, t4.c4 as c4, X.c2 as c2 from t2 join t4 join t2 as X where t2.c1 = t4.c1 and t2.c1 = X.c1 and t4.c1 = X.c1",
expSQL: "select t2.c1 as c1, t2.c2 as c2, t4.c4 as c4, X.c2 as c2 from t2 join t4 on t2.c1 = t4.c1 join t2 as X on t2.c1 = t4.c1 and t2.c1 = X.c1 and t4.c1 = X.c1",
}, {
sql: "select * from t2 join t4 using (c1), t2 as t2b join t4 as t4b using (c1)",
expSQL: "select t2.c1 as c1, t2.c2 as c2, t4.c4 as c4, t2b.c1 as c1, t2b.c2 as c2, t4b.c4 as c4 from t2 join t4, t2 as t2b join t4 as t4b where t2b.c1 = t4b.c1 and t2.c1 = t4.c1",
expSQL: "select t2.c1 as c1, t2.c2 as c2, t4.c4 as c4, t2b.c1 as c1, t2b.c2 as c2, t4b.c4 as c4 from t2 join t4 on t2.c1 = t4.c1, t2 as t2b join t4 as t4b on t2b.c1 = t4b.c1",
frouioui marked this conversation as resolved.
Show resolved Hide resolved
}, {
sql: "select * from t1 join t5 using (b)",
expSQL: "select t1.b as b, t1.a as a, t1.c as c, t5.a as a from t1 join t5 where t1.b = t5.b",
expSQL: "select t1.b as b, t1.a as a, t1.c as c, t5.a as a from t1 join t5 on t1.b = t5.b",
expanded: "main.t1.a, main.t1.b, main.t1.c, main.t5.a",
}, {
sql: "select * from t1 join t5 using (b) having b = 12",
expSQL: "select t1.b as b, t1.a as a, t1.c as c, t5.a as a from t1 join t5 where t1.b = t5.b having b = 12",
expSQL: "select t1.b as b, t1.a as a, t1.c as c, t5.a as a from t1 join t5 on t1.b = t5.b having b = 12",
}, {
sql: "select 1 from t1 join t5 using (b) having b = 12",
expSQL: "select 1 from t1 join t5 where t1.b = t5.b having t1.b = 12",
expSQL: "select 1 from t1 join t5 on t1.b = t5.b having t1.b = 12",
}, {
sql: "select * from (select 12) as t",
expSQL: "select t.`12` from (select 12 from dual) as t",
Expand Down Expand Up @@ -265,7 +265,7 @@ func TestRewriteJoinUsingColumns(t *testing.T) {
expErr string
}{{
sql: "select 1 from t1 join t2 using (a) where a = 42",
expSQL: "select 1 from t1 join t2 where t1.a = t2.a and t1.a = 42",
expSQL: "select 1 from t1 join t2 on t1.a = t2.a where t1.a = 42",
}, {
sql: "select 1 from t1 join t2 using (a), t3 where a = 42",
expErr: "Column 'a' in field list is ambiguous",
Expand Down