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

planner: support the transformation framework of the cascades planner #7869

Merged
merged 32 commits into from
Dec 5, 2018
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
e9c6551
planner, executor: implement the framework of the cascades planner
zz-jason Oct 11, 2018
4a20f3d
add missing file
zz-jason Oct 11, 2018
b995d12
Merge branch 'master' of https://github.com/pingcap/tidb into cascade…
zz-jason Oct 15, 2018
908b706
Merge branch 'master' into cascades/framework
zz-jason Oct 15, 2018
8e0afe4
fix lint
zz-jason Oct 15, 2018
4f51098
support ExprIter
zz-jason Oct 15, 2018
0408ba5
Merge branch 'master' of https://github.com/pingcap/tidb into cascade…
zz-jason Oct 17, 2018
bfe110e
fix ci
zz-jason Oct 17, 2018
d1c48fe
Merge branch 'master' of https://github.com/pingcap/tidb into cascade…
zz-jason Oct 18, 2018
d1641a5
Merge branch 'master' into cascades/framework
eurekaka Oct 18, 2018
6a5e6d1
address comment
zz-jason Oct 19, 2018
13b139d
fix ci
zz-jason Oct 20, 2018
f7baae5
remove the error return value from "rule.Match"
zz-jason Oct 22, 2018
32402cd
address comment
zz-jason Oct 24, 2018
099d16e
Merge branch 'master' into cascades/framework
zz-jason Oct 24, 2018
7b1cde7
address comment
zz-jason Oct 25, 2018
0065746
address comment
zz-jason Oct 25, 2018
dbc38b2
address comment
zz-jason Oct 25, 2018
6ba5e16
fix lint
zz-jason Oct 25, 2018
cf6b6df
Merge branch 'master' into cascades/framework
eurekaka Oct 26, 2018
5c64b44
address comment
zz-jason Oct 26, 2018
2f5d850
Merge branch 'cascades/framework' of https://github.com/zz-jason/tidb…
zz-jason Oct 26, 2018
143435f
Merge branch 'master' into cascades/framework
alivxxx Oct 26, 2018
a49eeb4
add ut
zz-jason Oct 26, 2018
4f30a91
Merge branch 'master' into cascades/framework
zz-jason Oct 26, 2018
7431337
add ut for get first group expr
zz-jason Oct 26, 2018
151a513
Merge branch 'cascades/framework' of https://github.com/zz-jason/tidb…
zz-jason Oct 26, 2018
b4f2b50
Merge branch 'master' into cascades/framework
zz-jason Nov 22, 2018
d6ae1ec
Merge branch 'master' of https://github.com/pingcap/tidb into cascade…
zz-jason Dec 5, 2018
4937114
Merge branch 'master' into cascades/framework
winoros Dec 5, 2018
45bf31f
fix Reset()
zz-jason Dec 5, 2018
0d78dcf
Merge branch 'cascades/framework' of https://github.com/zz-jason/tidb…
zz-jason Dec 5, 2018
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
139 changes: 139 additions & 0 deletions planner/cascades/expr_iterator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// Copyright 2018 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package cascades

import (
"container/list"
)

// ExprIter enumerates all the equivalent expressions in the group according to
// the expression pattern.
type ExprIter struct {
// The group and element field solely identify a group expression.
group *Group
element *list.Element

// Indicates whether the current group expression binded by the iterator
// matches the pattern after the creation or iteration of the group.
foundMatch bool

// operand is the node of the pattern tree. The operand type of the group
// expression must be matched with it, otherwise the group expression is
// ignored during the iteration.
operand Operand

// children is used to iterate the child expressions.
children []*ExprIter
}

// Next returns the next group expression matches the pattern.
func (iter *ExprIter) Next() {
// Iterate child firstly.
for i := range iter.children {
iter.children[i].Next()
if iter.children[i].OK() {
alivxxx marked this conversation as resolved.
Show resolved Hide resolved
zz-jason marked this conversation as resolved.
Show resolved Hide resolved
iter.foundMatch = true
return
}
}

// It's root node.
if iter.group == nil {
iter.foundMatch = false
return
}

// Otherwise, iterate itself to find more matched equivalent expressions.
for iter.element.Next(); iter.element != nil; iter.element.Next() {
zz-jason marked this conversation as resolved.
Show resolved Hide resolved
expr := iter.element.Value.(*GroupExpr)
exprOperand := GetOperand(expr.exprNode)

if !iter.operand.match(exprOperand) {
continue
}

iter.foundMatch = true
for i := range iter.children {
if !iter.children[i].Reset(expr.children[i]) {
zz-jason marked this conversation as resolved.
Show resolved Hide resolved
iter.foundMatch = false
break
}
}
if iter.foundMatch {
return
}
}
iter.foundMatch = false
return
}

// OK returns whether the iterator founds a group expression matches the pattern.
func (iter *ExprIter) OK() bool {
zz-jason marked this conversation as resolved.
Show resolved Hide resolved
return iter.foundMatch
}

// Reset resets the iterator to the first matched group expression.
func (iter *ExprIter) Reset(g *Group) bool {
for elem := g.equivalents.Front(); elem != nil; elem.Next() {
zz-jason marked this conversation as resolved.
Show resolved Hide resolved
expr := elem.Value.(*GroupExpr)
if !iter.operand.match(GetOperand(expr.exprNode)) {
continue
}

allMatched := true
for i := range expr.children {
if !iter.children[i].Reset(expr.children[i]) {
alivxxx marked this conversation as resolved.
Show resolved Hide resolved
allMatched = false
break
}
}
if allMatched {
iter.group = g
iter.element = elem
return true
}
}
return false
}

// NewExprIter creates the iterator on the group which iterates the group
// expressions matches the pattern.
func NewExprIter(expr *GroupExpr, p *Pattern) *ExprIter {
if !p.operand.match(GetOperand(expr.exprNode)) {
return nil
zz-jason marked this conversation as resolved.
Show resolved Hide resolved
}

iter := &ExprIter{operand: p.operand, foundMatch: true}
for i := range p.children {
childIter := newChildExprIter(expr.children[i], p.children[i])
zz-jason marked this conversation as resolved.
Show resolved Hide resolved
if childIter == nil {
return nil
}
iter.children = append(iter.children, childIter)
}
return iter
}

func newChildExprIter(childGroup *Group, childPattern *Pattern) *ExprIter {
zz-jason marked this conversation as resolved.
Show resolved Hide resolved
for elem := childGroup.equivalents.Front(); elem != nil; elem = elem.Next() {
childIter := NewExprIter(elem.Value.(*GroupExpr), childPattern)
if childIter == nil {
continue
}
childIter.group = childGroup
childIter.element = elem
return childIter
}
return nil
}
124 changes: 124 additions & 0 deletions planner/cascades/optimize.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Copyright 2018 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package cascades

import (
plannercore "github.com/pingcap/tidb/planner/core"
"github.com/pingcap/tidb/sessionctx"
"github.com/pkg/errors"
)

// FindBestPlan is the optimization entrance of the cascades planner. The
// optimization is composed of 2 phases: exploration and implementation.
func FindBestPlan(sctx sessionctx.Context, logical plannercore.LogicalPlan) (plannercore.Plan, error) {
rootGroup := convert2Group(logical)

err := onPhaseExploration(sctx, rootGroup)
if err != nil {
return nil, err
}

best, err := onPhaseImplementation(sctx, rootGroup)
return best, err
}

// convert2Group converts a logical plan to expression groups.
func convert2Group(node plannercore.LogicalPlan) *Group {
e := NewGroupExpr(node)
e.children = make([]*Group, 0, len(node.Children()))
for _, child := range node.Children() {
childGroup := convert2Group(child)
e.children = append(e.children, childGroup)
}
return NewGroup(e)
}

func onPhaseExploration(sctx sessionctx.Context, g *Group) error {
for !g.explored {
err := exploreGroup(g)
if err != nil {
return err
}
}
return nil
}

func exploreGroup(g *Group) error {
if g.explored {
return nil
}

g.explored = true
for elem := g.equivalents.Front(); elem != nil; elem.Next() {
curExpr := elem.Value.(*GroupExpr)
if curExpr.explored {
continue
}

// Explore child groups firstly.
curExpr.explored = true
for _, childGroup := range curExpr.children {
exploreGroup(childGroup)
curExpr.explored = curExpr.explored && childGroup.explored
}

eraseCur, err := findMoreEquiv(curExpr, g)
eurekaka marked this conversation as resolved.
Show resolved Hide resolved
eurekaka marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return err
}
if eraseCur {
g.Delete(curExpr)
}
zz-jason marked this conversation as resolved.
Show resolved Hide resolved

g.explored = g.explored && curExpr.explored
}
return nil
}

// Find and apply the matched transformation rules.
func findMoreEquiv(expr *GroupExpr, g *Group) (eraseCur bool, err error) {
zz-jason marked this conversation as resolved.
Show resolved Hide resolved
for _, rule := range GetTransformationRules(expr.exprNode) {
pattern := rule.GetPattern()
zz-jason marked this conversation as resolved.
Show resolved Hide resolved
// Create a binding of the current group expression and the pattern of
// the transformation rule to enumerate all the possible expressions.
for iter := NewExprIter(expr, pattern); iter != nil && iter.OK(); iter.Next() {
zz-jason marked this conversation as resolved.
Show resolved Hide resolved
matched, err := rule.Match(iter)
if err != nil {
return false, err
}

if !matched {
continue
}

newExpr, erase, err := rule.OnTransform(iter)
eraseCur = eraseCur || erase
if !g.Insert(newExpr) {
continue
}

// If the new group expression is successfully inserted into the
// current group, we mark the group expression and the group as
// unexplored to enable the exploration on the new group expression
// and all the antecedent groups.
newExpr.explored = false
g.explored = false
}
}
return eraseCur, nil
}

func onPhaseImplementation(sctx sessionctx.Context, g *Group) (plannercore.Plan, error) {
return nil, errors.New("the onPhaseImplementation() of the cascades planner is not implemented")
}
34 changes: 17 additions & 17 deletions planner/cascades/pattern.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,38 +60,38 @@ const (
)

// GetOperand maps logical plan operator to Operand.
func GetOperand(p plannercore.LogicalPlan) (Operand, error) {
switch x := p.(type) {
func GetOperand(p plannercore.LogicalPlan) Operand {
switch p.(type) {
case *plannercore.LogicalJoin:
return OperandJoin, nil
return OperandJoin
case *plannercore.LogicalAggregation:
return OperandAggregation, nil
return OperandAggregation
case *plannercore.LogicalProjection:
return OperandProjection, nil
return OperandProjection
case *plannercore.LogicalSelection:
return OperandSelection, nil
return OperandSelection
case *plannercore.LogicalApply:
return OperandApply, nil
return OperandApply
case *plannercore.LogicalMaxOneRow:
return OperandMaxOneRow, nil
return OperandMaxOneRow
case *plannercore.LogicalTableDual:
return OperandTableDual, nil
return OperandTableDual
case *plannercore.DataSource:
return OperandDataSource, nil
return OperandDataSource
case *plannercore.LogicalUnionScan:
return OperandUnionScan, nil
return OperandUnionScan
case *plannercore.LogicalUnionAll:
return OperandUnionAll, nil
return OperandUnionAll
case *plannercore.LogicalSort:
return OperandSort, nil
return OperandSort
case *plannercore.LogicalTopN:
return OperandTopN, nil
return OperandTopN
case *plannercore.LogicalLock:
return OperandLock, nil
return OperandLock
case *plannercore.LogicalLimit:
return OperandLimit, nil
return OperandLimit
default:
return OperandUnsupported, plannercore.ErrUnsupportedType.GenWithStack("Unsupported LogicalPlan(%T) for GetOperand", x)
return OperandUnsupported
}
}

Expand Down
42 changes: 42 additions & 0 deletions planner/cascades/transformation_rules.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2018 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package cascades

import (
plannercore "github.com/pingcap/tidb/planner/core"
)

// Transformation defines the interface for the transformation rules.
type Transformation interface {
GetPattern() *Pattern
Match(expr *ExprIter) (matched bool, err error)
zz-jason marked this conversation as resolved.
Show resolved Hide resolved
OnTransform(old *ExprIter) (new *GroupExpr, eraseOld bool, err error)
}

// GetTransformationRules gets the all the candidate transformation rules based
// on the logical plan node.
func GetTransformationRules(node plannercore.LogicalPlan) []Transformation {
return transformationMap[GetOperand(node)]
}

var transformationMap = map[Operand][]Transformation{
/**
operandSelect: []Transformation{
nil,
},
operandProject: []Transformation{
nil,
},
*/
}
3 changes: 2 additions & 1 deletion planner/optimize.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package planner
import (
"github.com/pingcap/tidb/ast"
"github.com/pingcap/tidb/infoschema"
"github.com/pingcap/tidb/planner/cascades"
plannercore "github.com/pingcap/tidb/planner/core"
"github.com/pingcap/tidb/privilege"
"github.com/pingcap/tidb/sessionctx"
Expand Down Expand Up @@ -62,7 +63,7 @@ func Optimize(ctx sessionctx.Context, node ast.Node, is infoschema.InfoSchema) (

// Handle the logical plan statement, use cascades planner if enabled.
if ctx.GetSessionVars().EnableCascadesPlanner {
return nil, errors.New("the cascades planner is not implemented yet")
return cascades.FindBestPlan(ctx, logic)
}
return plannercore.DoOptimize(builder.GetOptFlag(), logic)
}
Expand Down