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

Gen4: improvement of joinTree creation #8822

Merged
merged 12 commits into from
Sep 18, 2021
Merged
1 change: 1 addition & 0 deletions go/tools/sizegen/integration/cached_size.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions go/vt/vtgate/engine/cached_size.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions go/vt/vtgate/planbuilder/plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,23 @@ func TestOne(t *testing.T) {
testFile(t, "onecase.txt", "", vschema, true)
}

func TestRubyOnRailsQueries(t *testing.T) {
vschemaWrapper := &vschemaWrapper{
v: loadSchema(t, "rails_schema_test.json"),
sysVarEnabled: true,
}

testOutputTempDir, err := ioutil.TempDir("", "plan_test")
require.NoError(t, err)
defer func() {
if !t.Failed() {
os.RemoveAll(testOutputTempDir)
}
}()

testFile(t, "rails_cases.txt", testOutputTempDir, vschemaWrapper, true)
}

func TestOLTP(t *testing.T) {
vschemaWrapper := &vschemaWrapper{
v: loadSchema(t, "oltp_schema_test.json"),
Expand Down
153 changes: 95 additions & 58 deletions go/vt/vtgate/planbuilder/route_planning.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,82 +405,119 @@ func stripDownQuery(from, to sqlparser.SelectStatement) error {
}

func pushJoinPredicate(ctx *planningContext, exprs []sqlparser.Expr, tree queryTree) (queryTree, error) {
if len(exprs) == 0 {
return tree, nil
}
switch node := tree.(type) {
case *routeTree:
plan := node.clone().(*routeTree)
err := plan.addPredicate(ctx, exprs...)
return pushJoinPredicateOnRoute(ctx, exprs, node)
case *joinTree:
return pushJoinPredicateOnJoin(ctx, exprs, node)
case *derivedTree:
return pushJoinPredicateOnDerived(ctx, exprs, node)
case *vindexTree:
// vindexFunc cannot accept predicates from the other side of a join
return node, nil
default:
panic(fmt.Sprintf("BUG: unknown type %T", node))
}
}

func pushJoinPredicateOnRoute(ctx *planningContext, exprs []sqlparser.Expr, node *routeTree) (queryTree, error) {
plan := node.clone().(*routeTree)
err := plan.addPredicate(ctx, exprs...)
if err != nil {
return nil, err
}
return plan, nil
}

func pushJoinPredicateOnDerived(ctx *planningContext, exprs []sqlparser.Expr, node *derivedTree) (queryTree, error) {
plan := node.clone().(*derivedTree)

newExpressions := make([]sqlparser.Expr, 0, len(exprs))
for _, expr := range exprs {
tblInfo, err := ctx.semTable.TableInfoForExpr(expr)
if err != nil {
return nil, err
}
return plan, nil
rewritten, err := semantics.RewriteDerivedExpression(expr, tblInfo)
if err != nil {
return nil, err
}
newExpressions = append(newExpressions, rewritten)
}

case *joinTree:
node = node.clone().(*joinTree)

// we break up the predicates so that colnames from the LHS are replaced by arguments
var rhsPreds []sqlparser.Expr
var lhsColumns []*sqlparser.ColName
var lhsVarsName []string
lhsSolves := node.lhs.tableID()
for _, expr := range exprs {
bvName, cols, predicate, err := breakPredicateInLHSandRHS(expr, ctx.semTable, lhsSolves)
if err != nil {
return nil, err
}
lhsColumns = append(lhsColumns, cols...)
lhsVarsName = append(lhsVarsName, bvName...)
rhsPreds = append(rhsPreds, predicate)
newInner, err := pushJoinPredicate(ctx, newExpressions, plan.inner)
if err != nil {
return nil, err
}

plan.inner = newInner
return plan, nil
}

func pushJoinPredicateOnJoin(ctx *planningContext, exprs []sqlparser.Expr, node *joinTree) (queryTree, error) {
node = node.clone().(*joinTree)

var rhsPreds []sqlparser.Expr
var lhsPreds []sqlparser.Expr
var lhsColumns []*sqlparser.ColName
var lhsVarsName []string

for _, expr := range exprs {
// We find the dependencies for the given expression and if they are solved entirely by one
// side of the join tree, then we push the predicate there and do not break it into parts.
// In case a predicate has no dependencies, then it is pushed to both sides so that we can filter
// rows as early as possible making join cheaper on the vtgate level.
depsForExpr := ctx.semTable.RecursiveDeps(expr)
singleSideDeps := false
if depsForExpr.IsSolvedBy(node.lhs.tableID()) {
lhsPreds = append(lhsPreds, expr)
singleSideDeps = true
}
if lhsColumns != nil && lhsVarsName != nil {
idxs, err := node.pushOutputColumns(lhsColumns, ctx.semTable)
if err != nil {
return nil, err
}
for i, idx := range idxs {
node.vars[lhsVarsName[i]] = idx
}
if depsForExpr.IsSolvedBy(node.rhs.tableID()) {
rhsPreds = append(rhsPreds, expr)
singleSideDeps = true
}

rhsPlan, err := pushJoinPredicate(ctx, rhsPreds, node.rhs)
if err != nil {
return nil, err
if singleSideDeps {
continue
}

return &joinTree{
lhs: node.lhs,
rhs: rhsPlan,
outer: node.outer,
vars: node.vars,
}, nil
case *derivedTree:
plan := node.clone().(*derivedTree)

newExpressions := make([]sqlparser.Expr, 0, len(exprs))
for _, expr := range exprs {
tblInfo, err := ctx.semTable.TableInfoForExpr(expr)
if err != nil {
return nil, err
}
rewritten, err := semantics.RewriteDerivedExpression(expr, tblInfo)
if err != nil {
return nil, err
}
newExpressions = append(newExpressions, rewritten)
bvName, cols, predicate, err := breakPredicateInLHSandRHS(expr, ctx.semTable, node.lhs.tableID())
if err != nil {
return nil, err
}
lhsColumns = append(lhsColumns, cols...)
lhsVarsName = append(lhsVarsName, bvName...)
rhsPreds = append(rhsPreds, predicate)
}

newInner, err := pushJoinPredicate(ctx, newExpressions, plan.inner)
if lhsColumns != nil && lhsVarsName != nil {
idxs, err := node.pushOutputColumns(lhsColumns, ctx.semTable)
if err != nil {
return nil, err
}
for i, idx := range idxs {
node.vars[lhsVarsName[i]] = idx
}
}
lhsPlan, err := pushJoinPredicate(ctx, lhsPreds, node.lhs)
if err != nil {
return nil, err
}

plan.inner = newInner
return plan, nil
case *vindexTree:
// vindexFunc cannot accept predicates from the other side of a join
return node, nil
default:
panic(fmt.Sprintf("BUG: unknown type %T", node))
rhsPlan, err := pushJoinPredicate(ctx, rhsPreds, node.rhs)
if err != nil {
return nil, err
}
return &joinTree{
lhs: lhsPlan,
rhs: rhsPlan,
outer: node.outer,
vars: node.vars,
}, nil
}

func breakPredicateInLHSandRHS(
Expand Down
19 changes: 10 additions & 9 deletions go/vt/vtgate/planbuilder/testdata/from_cases.txt
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,7 @@ Gen4 plan same as above
"Sharded": true
},
"FieldQuery": "select e.col from user_extra as e where 1 != 1",
"Query": "select e.col from user_extra as e",
"Query": "select e.col from user_extra as e where e.col = :user_col",
"Table": "user_extra"
},
{
Expand All @@ -653,7 +653,7 @@ Gen4 plan same as above
"Sharded": false
},
"FieldQuery": "select 1 from unsharded as m1 where 1 != 1",
"Query": "select 1 from unsharded as m1 where m1.col = :e_col and :user_col = :e_col",
"Query": "select 1 from unsharded as m1 where m1.col = :e_col",
"Table": "unsharded"
}
]
Expand Down Expand Up @@ -2605,21 +2605,22 @@ Gen4 plan same as above
"OperatorType": "Join",
"Variant": "Join",
"JoinColumnIndexes": "-1,-2",
"JoinVars": {
"user_id": 0
},
"TableName": "`user`_user_extra",
"Inputs": [
{
"OperatorType": "Route",
"Variant": "SelectScatter",
"Variant": "SelectEqualUnique",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"FieldQuery": "select `user`.id, `user`.col1 from `user` where 1 != 1",
"Query": "select `user`.id, `user`.col1 from `user`",
"Table": "`user`"
"Query": "select `user`.id, `user`.col1 from `user` where `user`.id = :ua_id",
"Table": "`user`",
"Values": [
":ua_id"
],
"Vindex": "user_index"
},
{
"OperatorType": "Route",
Expand All @@ -2629,7 +2630,7 @@ Gen4 plan same as above
"Sharded": true
},
"FieldQuery": "select 1 from user_extra where 1 != 1",
"Query": "select 1 from user_extra where :user_id = :ua_id",
"Query": "select 1 from user_extra",
"Table": "user_extra"
}
]
Expand Down
Loading