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 6 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
122 changes: 122 additions & 0 deletions planner/cascades/expr_iterator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// 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

// 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 int

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

func (iter *ExprIter) Next() bool {
// Iterate child firstly.
for i := range iter.children {
if iter.children[i].Next() {
return true
}
}

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

// 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 := expr.exprNode.GetOperand()

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

allMatched := 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
allMatched = false
break
}
}
if allMatched {
return true
}
}
return false
}

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(expr.exprNode.GetOperand()) {
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
}

func NewExprIter(expr *GroupExpr, p *Pattern) *ExprIter {
if !p.operand.match(expr.exprNode.GetOperand()) {
return nil
}

iter := &ExprIter{operand: p.operand}
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
}
72 changes: 72 additions & 0 deletions planner/cascades/group.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// 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"
"fmt"
)

// Group is short for expression group, which is used to store all the
// logically equivalent expressions. It's a set of GroupExpr.
type Group struct {
equivalents *list.List
fingerprints map[string]*list.Element

explored bool
selfFingerprint string
}

// NewGroup creates a new Group.
func NewGroup(e *GroupExpr) *Group {
g := &Group{
equivalents: list.New(),
fingerprints: make(map[string]*list.Element),
}
g.Insert(e)
return g
}

// FingerPrint returns the unique fingerprint of the group.
func (g *Group) FingerPrint() string {
if g.selfFingerprint == "" {
g.selfFingerprint = fmt.Sprintf("%p", g)
}
return g.selfFingerprint
}

// Insert a nonexistent group exxpression.
func (g *Group) Insert(e *GroupExpr) bool {
if g.Exists(e) {
return false
}
newEquiv := g.equivalents.PushBack(e)
g.fingerprints[e.FingerPrint()] = newEquiv
return true
}

// Delete an existing group expression.
func (g *Group) Delete(e *GroupExpr) {
fingerprint := e.FingerPrint()
if equiv, ok := g.fingerprints[fingerprint]; ok {
g.equivalents.Remove(equiv)
delete(g.fingerprints, fingerprint)
}
}

// Exists checks whether a group expression existed in a Group.
func (g *Group) Exists(e *GroupExpr) bool {
_, ok := g.fingerprints[e.FingerPrint()]
return ok
}
53 changes: 53 additions & 0 deletions planner/cascades/group_expr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// 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 (
"fmt"

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

// GroupExpr is used to store all the logically equivalent expressions which
// have the same root operator. Different from a normal expression, the
// children of a group expression are expression Groups, not expressions.
// Another property of group expression is that the child group references will
// never be changed once the group expression is created.
type GroupExpr struct {
exprNode plannercore.LogicalPlan
children []*Group
explored bool

selfFingerprint string
}

// NewGroupExpr creates a GroupExpr based on a logical plan node.
func NewGroupExpr(node plannercore.LogicalPlan) *GroupExpr {
return &GroupExpr{
exprNode: node,
children: nil,
explored: false,
}
}

// FingerPrint get the unique fingerprint of the group expression.
func (e *GroupExpr) FingerPrint() string {
if e.selfFingerprint == "" {
e.selfFingerprint = fmt.Sprintf("%v", e.exprNode.ID())
for i := range e.children {
e.selfFingerprint += e.children[i].FingerPrint()
}
}
return e.selfFingerprint
}
129 changes: 129 additions & 0 deletions planner/cascades/optimize.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// 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(cur *GroupExpr, curGroup *Group) (eraseCur bool, err error) {
for _, rule := range GetTransformationRules(cur.exprNode) {
// Create a binding of the current group expression and the pattern of
// the transformation rule to enumerate all the possible expressions.
exprIter := NewExprIter(cur, rule.GetPattern())

// the transformation rule can not matche any expression in the group.
if exprIter == nil {
continue
}

for exprIter.Next() {
matched, err := rule.Match(exprIter)
if err != nil {
return false, err
}
if !matched {
continue
}

newExpr, erase, err := rule.OnTransform(exprIter)
eraseCur = eraseCur || erase
if !curGroup.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
curGroup.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")
}
Loading