Skip to content

Commit

Permalink
Addition of the Operator interface and deltion of outer joins from Qu…
Browse files Browse the repository at this point in the history
…eryGraph

Signed-off-by: Florent Poinsard <[email protected]>
  • Loading branch information
frouioui committed Jun 16, 2021
1 parent 04179d2 commit 545c5db
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 129 deletions.
25 changes: 25 additions & 0 deletions go/vt/vtgate/planbuilder/abstract/operator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
Copyright 2021 The Vitess Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package abstract

import "vitess.io/vitess/go/vt/vtgate/semantics"

type (
Operator interface {
TableID() semantics.TableSet
}
)
36 changes: 36 additions & 0 deletions go/vt/vtgate/planbuilder/abstract/outerjoin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
Copyright 2021 The Vitess Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package abstract

import (
"vitess.io/vitess/go/vt/sqlparser"
"vitess.io/vitess/go/vt/vtgate/semantics"
)

type (
OuterJoin struct {
Inner, Outer Operator
Exp sqlparser.Expr
}
)

func (oj *OuterJoin) TableID() semantics.TableSet {
var ts semantics.TableSet
ts = ts.Merge(oj.Inner.TableID())
ts = ts.Merge(oj.Outer.TableID())
return ts
}
41 changes: 10 additions & 31 deletions go/vt/vtgate/planbuilder/abstract/querygraph.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2020 The Vitess Authors.
Copyright 2021 The Vitess Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -39,8 +39,6 @@ type (
// innerJoins contains the predicates that need multiple Tables
innerJoins map[semantics.TableSet][]sqlparser.Expr

OuterJoins map[outerJoinTables][]sqlparser.Expr

// noDeps contains the predicates that can be evaluated anywhere.
noDeps sqlparser.Expr

Expand All @@ -61,6 +59,14 @@ type (
}
)

func (qg *QueryGraph) TableID() semantics.TableSet {
var ts semantics.TableSet
for _, table := range qg.Tables {
ts = ts.Merge(table.TableID)
}
return ts
}

func (qg *QueryGraph) GetPredicates(lhs, rhs semantics.TableSet) []sqlparser.Expr {
var allExprs []sqlparser.Expr
for tableSet, exprs := range qg.innerJoins {
Expand All @@ -73,16 +79,6 @@ func (qg *QueryGraph) GetPredicates(lhs, rhs semantics.TableSet) []sqlparser.Exp
return allExprs
}

func (qg *QueryGraph) GetOuterJoins(lhs, rhs semantics.TableSet) []sqlparser.Expr {
var allExprs []sqlparser.Expr
for tableSet, exprs := range qg.OuterJoins {
if tableSet.Outer.IsSolvedBy(rhs) && tableSet.inner.IsSolvedBy(lhs) {
allExprs = append(allExprs, exprs...)
}
}
return allExprs
}

func CreateQGFromSelect(sel *sqlparser.Select, semTable *semantics.SemTable) (*QueryGraph, error) {
qg := newQueryGraph()
if err := qg.collectTables(sel.From, semTable); err != nil {
Expand Down Expand Up @@ -129,7 +125,6 @@ func CreateQGFromSelectStatement(selStmt sqlparser.SelectStatement, semTable *se
func newQueryGraph() *QueryGraph {
return &QueryGraph{
innerJoins: map[semantics.TableSet][]sqlparser.Expr{},
OuterJoins: map[outerJoinTables][]sqlparser.Expr{},
Subqueries: map[*sqlparser.Subquery][]*QueryGraph{},
}
}
Expand Down Expand Up @@ -209,23 +204,7 @@ func (qg *QueryGraph) collectPredicateTable(t sqlparser.TableExpr, predicate sql
}
qg.innerJoins[deps] = allPredicates
case sqlparser.LeftJoinType, sqlparser.RightJoinType:
var outerJoinTable outerJoinTables
var err error
if table.Join == sqlparser.LeftJoinType {
outerJoinTable, err = qg.getOuterJoinTables(table.LeftExpr, table.RightExpr, right, left, semTable)
if err != nil {
return err
}
} else {
outerJoinTable, err = qg.getOuterJoinTables(table.RightExpr, table.LeftExpr, left, right, semTable)
if err != nil {
return err
}
}

allPredicates := qg.OuterJoins[outerJoinTable]
allPredicates = append(allPredicates, predicate)
qg.OuterJoins[outerJoinTable] = allPredicates
break
}
}
}
Expand Down
32 changes: 2 additions & 30 deletions go/vt/vtgate/planbuilder/abstract/querygraph_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,8 @@ func (qt *QueryTable) testString() string {
func (qg *QueryGraph) testString() string {
return fmt.Sprintf(`{
Tables:
%s%s%s%s%s
}`, strings.Join(qg.tableNames(), "\n"), qg.crossPredicateString(), qg.outerJoinsString(), qg.noDepsString(), qg.subqueriesString())
%s%s%s%s
}`, strings.Join(qg.tableNames(), "\n"), qg.crossPredicateString(), qg.noDepsString(), qg.subqueriesString())
}

func (qg *QueryGraph) crossPredicateString() string {
Expand All @@ -252,34 +252,6 @@ func (qg *QueryGraph) crossPredicateString() string {
return fmt.Sprintf("\nJoinPredicates:\n%s", strings.Join(joinPreds, "\n"))
}

func (qg *QueryGraph) outerJoinsString() string {
if len(qg.OuterJoins) == 0 {
return ""
}
var joinPreds []string
for deps, predicates := range qg.OuterJoins {
var inner []string
for _, id := range deps.inner.Constituents() {
inner = append(inner, fmt.Sprintf("%d", id))
}
var outer []string
for _, id := range deps.Outer.Constituents() {
outer = append(outer, fmt.Sprintf("%d", id))
}

var expressions []string
for _, expr := range predicates {
expressions = append(expressions, sqlparser.String(expr))
}
tables := fmt.Sprintf("inner: %s, outer: %s", strings.Join(inner, ":"), strings.Join(outer, ":"))

exprConcat := strings.Join(expressions, " and ")
joinPreds = append(joinPreds, fmt.Sprintf("\t%s - %s", tables, exprConcat))
}
sort.Strings(joinPreds)
return fmt.Sprintf("\nOuterJoins:\n%s", strings.Join(joinPreds, "\n"))
}

func (qg *QueryGraph) tableNames() []string {
var tables []string
for _, t := range qg.Tables {
Expand Down
79 changes: 11 additions & 68 deletions go/vt/vtgate/planbuilder/route_planning.go
Original file line number Diff line number Diff line change
Expand Up @@ -680,13 +680,8 @@ func breakPredicateInLHSandRHS(expr sqlparser.Expr, semTable *semantics.SemTable
return
}

func mergeOrJoin(lhs, rhs joinTree, joinPredicates []sqlparser.Expr, semTable *semantics.SemTable, inner bool) (joinTree, error) {
var newPlan joinTree
if inner {
newPlan = tryInnerMerge(lhs, rhs, joinPredicates, semTable)
} else {
newPlan = tryOuterMerge(lhs, rhs, joinPredicates, semTable)
}
func mergeOrJoin(lhs, rhs joinTree, joinPredicates []sqlparser.Expr, semTable *semantics.SemTable) (joinTree, error) {
newPlan := tryInnerMerge(lhs, rhs, joinPredicates, semTable)
if newPlan != nil {
return newPlan, nil
}
Expand Down Expand Up @@ -715,37 +710,19 @@ func greedySolve(qg *abstract.QueryGraph, semTable *semantics.SemTable, vschema
return nil, err
}

var innerJt, outerJt []joinTree
outers := semantics.TableSet(0)
for key := range qg.OuterJoins {
outers = outers.Merge(key.Outer)
}
for _, jt := range joinTrees {
if !jt.tables().IsOverlapping(outers) {
innerJt = append(innerJt, jt)
} else {
outerJt = append(outerJt, jt)
}
}

innerTree, err := mergeJoinTrees(qg, semTable, innerJt, planCache, true, false)
if err != nil {
return nil, err
}
outerJt = append(outerJt, innerTree)
tree, err := mergeJoinTrees(qg, semTable, outerJt, planCache, false, false)
tree, err := mergeJoinTrees(qg, semTable, joinTrees, planCache, false)
if err != nil {
return nil, err
}
return tree, nil
}

func mergeJoinTrees(qg *abstract.QueryGraph, semTable *semantics.SemTable, joinTrees []joinTree, planCache cacheMap, inner, crossJoinsOK bool) (joinTree, error) {
func mergeJoinTrees(qg *abstract.QueryGraph, semTable *semantics.SemTable, joinTrees []joinTree, planCache cacheMap, crossJoinsOK bool) (joinTree, error) {
if len(joinTrees) == 0 {
return nil, nil
}
for len(joinTrees) > 1 {
bestTree, lIdx, rIdx, err := findBestJoinTree(qg, semTable, joinTrees, planCache, inner, crossJoinsOK)
bestTree, lIdx, rIdx, err := findBestJoinTree(qg, semTable, joinTrees, planCache, crossJoinsOK)
if err != nil {
return nil, err
}
Expand All @@ -770,14 +747,14 @@ func mergeJoinTrees(qg *abstract.QueryGraph, semTable *semantics.SemTable, joinT
return joinTrees[0], nil
}

func (cm cacheMap) getJoinTreeFor(lhs, rhs joinTree, joinPredicates []sqlparser.Expr, semTable *semantics.SemTable, inner bool) (joinTree, error) {
func (cm cacheMap) getJoinTreeFor(lhs, rhs joinTree, joinPredicates []sqlparser.Expr, semTable *semantics.SemTable) (joinTree, error) {
solves := tableSetPair{left: lhs.tables(), right: rhs.tables()}
cachedPlan := cm[solves]
if cachedPlan != nil {
return cachedPlan, nil
}

join, err := mergeOrJoin(lhs, rhs, joinPredicates, semTable, inner)
join, err := mergeOrJoin(lhs, rhs, joinPredicates, semTable)
if err != nil {
return nil, err
}
Expand All @@ -790,26 +767,21 @@ func findBestJoinTree(
semTable *semantics.SemTable,
plans []joinTree,
planCache cacheMap,
inner, crossJoinsOK bool,
crossJoinsOK bool,
) (bestPlan joinTree, lIdx int, rIdx int, err error) {
for i, lhs := range plans {
for j, rhs := range plans {
if i == j {
continue
}
var joinPredicates []sqlparser.Expr
if inner {
joinPredicates = qg.GetPredicates(lhs.tables(), rhs.tables())
} else {
joinPredicates = qg.GetOuterJoins(lhs.tables(), rhs.tables())
}
joinPredicates := qg.GetPredicates(lhs.tables(), rhs.tables())
if len(joinPredicates) == 0 && !crossJoinsOK {
// if there are no predicates joining the two tables,
// creating a join between them would produce a
// cartesian product, which is almost always a bad idea
continue
}
plan, err := planCache.getJoinTreeFor(lhs, rhs, joinPredicates, semTable, inner)
plan, err := planCache.getJoinTreeFor(lhs, rhs, joinPredicates, semTable)
if err != nil {
return nil, 0, 0, err
}
Expand Down Expand Up @@ -838,7 +810,7 @@ func leftToRightSolve(qg *abstract.QueryGraph, semTable *semantics.SemTable, vsc
}
joinPredicates := qg.GetPredicates(acc.tables(), plan.tables())
// TODO: handle outer
acc, err = mergeOrJoin(acc, plan, joinPredicates, semTable, true)
acc, err = mergeOrJoin(acc, plan, joinPredicates, semTable)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -993,21 +965,6 @@ func tryInnerMerge(a, b joinTree, joinPredicates []sqlparser.Expr, semTable *sem
return r
}

func tryOuterMerge(a, b joinTree, joinPredicates []sqlparser.Expr, semTable *semantics.SemTable) joinTree {
aRoute, bRoute := joinTreesToRoutes(a, b)
if aRoute == nil || bRoute == nil {
return nil
}

newTabletSet := aRoute.solved | bRoute.solved
r := createRoutePlanForOuter(aRoute, bRoute, newTabletSet, joinPredicates)

if ok := ensureMerge(aRoute, bRoute, r, joinPredicates, semTable); !ok {
return nil
}
return r
}

func ensureMerge(aRoute, bRoute, r *routePlan, joinPredicates []sqlparser.Expr, semTable *semantics.SemTable) bool {
switch aRoute.routeOpCode {
case engine.SelectUnsharded, engine.SelectDBA:
Expand Down Expand Up @@ -1059,20 +1016,6 @@ func createRoutePlanForInner(aRoute *routePlan, newTabletSet semantics.TableSet,
}
}

func createRoutePlanForOuter(aRoute, bRoute *routePlan, newTabletSet semantics.TableSet, joinPredicates []sqlparser.Expr) *routePlan {
return &routePlan{
routeOpCode: aRoute.routeOpCode,
solved: newTabletSet,
leftJoins: append(aRoute.leftJoins, leftJoin{
left: aRoute._tables[len(aRoute._tables)-1],
right: bRoute._tables[0],
expr: joinPredicates[0],
}),
keyspace: aRoute.keyspace,
vindexPreds: append(aRoute.vindexPreds, bRoute.vindexPreds...),
}
}

var _ sort.Interface = (routeTables)(nil)

func (r routeTables) Len() int {
Expand Down

0 comments on commit 545c5db

Please sign in to comment.