Skip to content

Commit

Permalink
planner: support the transformation framework of the cascades planner (
Browse files Browse the repository at this point in the history
  • Loading branch information
zz-jason authored Dec 5, 2018
1 parent 93a18df commit f06f3ca
Show file tree
Hide file tree
Showing 4 changed files with 467 additions and 1 deletion.
176 changes: 176 additions & 0 deletions planner/cascades/expr_iterator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
// 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 {
// group and element solely identify a group expression.
group *Group
element *list.Element

// matched indicates whether the current group expression binded by the
// iterator matches the pattern after the creation or iteration.
matched bool

// operand is the node of the pattern tree. The operand type of the group
// expression must be matched with it.
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() (found bool) {
defer func() {
iter.matched = found
}()

// Iterate child firstly.
for i := len(iter.children) - 1; i >= 0; i-- {
if !iter.children[i].Next() {
continue
}

for j := i + 1; j < len(iter.children); j++ {
iter.children[j].Reset()
}
return true
}

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

// Otherwise, iterate itself to find more matched equivalent expressions.
for elem := iter.element.Next(); elem != nil; elem = elem.Next() {
expr := elem.Value.(*GroupExpr)
exprOperand := GetOperand(expr.exprNode)

if !iter.operand.match(exprOperand) {
// All the equivalents which have the same operand are continuously
// stored in the list. Once the current equivalent can not match
// the operand, the rest can not, either.
return false
}

if len(iter.children) != len(expr.children) {
continue
}

allMatched := true
for i := range iter.children {
iter.children[i].group = expr.children[i]
if !iter.children[i].Reset() {
allMatched = false
break
}
}

if allMatched {
iter.element = elem
return true
}
}
return false
}

// Matched returns whether the iterator founds a group expression matches the
// pattern.
func (iter *ExprIter) Matched() bool {
return iter.matched
}

// Reset resets the iterator to the first matched group expression.
func (iter *ExprIter) Reset() (findMatch bool) {
defer func() { iter.matched = findMatch }()

for elem := iter.group.GetFirstElem(iter.operand); elem != nil; elem = elem.Next() {
expr := elem.Value.(*GroupExpr)
exprOperand := GetOperand(expr.exprNode)
if !iter.operand.match(exprOperand) {
break
}

if len(expr.children) != len(iter.children) {
continue
}

allMatched := true
for i := range iter.children {
iter.children[i].group = expr.children[i]
if !iter.children[i].Reset() {
allMatched = false
break
}
}
if allMatched {
iter.element = elem
return true
}
}
return false
}

// NewExprIterFromGroupElem creates the iterator on the group element.
func NewExprIterFromGroupElem(elem *list.Element, p *Pattern) *ExprIter {
expr := elem.Value.(*GroupExpr)
if !p.operand.match(GetOperand(expr.exprNode)) {
return nil
}
iter := newExprIterFromGroupExpr(expr, p)
if iter != nil {
iter.element = elem
}
return iter
}

// newExprIterFromGroupExpr creates the iterator on the group expression.
func newExprIterFromGroupExpr(expr *GroupExpr, p *Pattern) *ExprIter {
if len(p.children) != len(expr.children) {
return nil
}

iter := &ExprIter{operand: p.operand, matched: true}
for i := range p.children {
childIter := newExprIterFromGroup(expr.children[i], p.children[i])
if childIter == nil {
return nil
}
iter.children = append(iter.children, childIter)
}
return iter
}

// newExprIterFromGroup creates the iterator on the group.
func newExprIterFromGroup(g *Group, p *Pattern) *ExprIter {
for elem := g.GetFirstElem(p.operand); elem != nil; elem = elem.Next() {
expr := elem.Value.(*GroupExpr)
if !p.operand.match(GetOperand(expr.exprNode)) {
return nil
}
iter := newExprIterFromGroupExpr(expr, p)
if iter != nil {
iter.group, iter.element = g, elem
return iter
}
}
return nil
}
171 changes: 171 additions & 0 deletions planner/cascades/expr_iterator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// 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 (
. "github.com/pingcap/check"
plannercore "github.com/pingcap/tidb/planner/core"
)

func (s *testCascadesSuite) TestNewExprIterFromGroupElem(c *C) {
g0 := NewGroup(NewGroupExpr(plannercore.LogicalSelection{}.Init(s.sctx)))
g0.Insert(NewGroupExpr(plannercore.LogicalLimit{}.Init(s.sctx)))
g0.Insert(NewGroupExpr(plannercore.LogicalProjection{}.Init(s.sctx)))
g0.Insert(NewGroupExpr(plannercore.LogicalLimit{}.Init(s.sctx)))

g1 := NewGroup(NewGroupExpr(plannercore.LogicalSelection{}.Init(s.sctx)))
g1.Insert(NewGroupExpr(plannercore.LogicalLimit{}.Init(s.sctx)))
g1.Insert(NewGroupExpr(plannercore.LogicalProjection{}.Init(s.sctx)))
g1.Insert(NewGroupExpr(plannercore.LogicalLimit{}.Init(s.sctx)))

expr := NewGroupExpr(plannercore.LogicalJoin{}.Init(s.sctx))
expr.children = append(expr.children, g0)
expr.children = append(expr.children, g1)
g2 := NewGroup(expr)

pattern := BuildPattern(OperandJoin, BuildPattern(OperandProjection), BuildPattern(OperandSelection))
iter := NewExprIterFromGroupElem(g2.equivalents.Front(), pattern)

c.Assert(iter, NotNil)
c.Assert(iter.group, IsNil)
c.Assert(iter.element, Equals, g2.equivalents.Front())
c.Assert(iter.matched, Equals, true)
c.Assert(iter.operand, Equals, OperandJoin)
c.Assert(len(iter.children), Equals, 2)

c.Assert(iter.children[0].group, Equals, g0)
c.Assert(iter.children[0].element, Equals, g0.GetFirstElem(OperandProjection))
c.Assert(iter.children[0].matched, Equals, true)
c.Assert(iter.children[0].operand, Equals, OperandProjection)
c.Assert(len(iter.children[0].children), Equals, 0)

c.Assert(iter.children[1].group, Equals, g1)
c.Assert(iter.children[1].element, Equals, g1.GetFirstElem(OperandSelection))
c.Assert(iter.children[1].matched, Equals, true)
c.Assert(iter.children[1].operand, Equals, OperandSelection)
c.Assert(len(iter.children[0].children), Equals, 0)
}

func (s *testCascadesSuite) TestExprIterNext(c *C) {
g0 := NewGroup(NewGroupExpr(plannercore.LogicalProjection{}.Init(s.sctx)))
g0.Insert(NewGroupExpr(plannercore.LogicalLimit{}.Init(s.sctx)))
g0.Insert(NewGroupExpr(plannercore.LogicalProjection{}.Init(s.sctx)))
g0.Insert(NewGroupExpr(plannercore.LogicalLimit{}.Init(s.sctx)))
g0.Insert(NewGroupExpr(plannercore.LogicalProjection{}.Init(s.sctx)))

g1 := NewGroup(NewGroupExpr(plannercore.LogicalSelection{}.Init(s.sctx)))
g1.Insert(NewGroupExpr(plannercore.LogicalLimit{}.Init(s.sctx)))
g1.Insert(NewGroupExpr(plannercore.LogicalSelection{}.Init(s.sctx)))
g1.Insert(NewGroupExpr(plannercore.LogicalLimit{}.Init(s.sctx)))
g1.Insert(NewGroupExpr(plannercore.LogicalSelection{}.Init(s.sctx)))

expr := NewGroupExpr(plannercore.LogicalJoin{}.Init(s.sctx))
expr.children = append(expr.children, g0)
expr.children = append(expr.children, g1)
g2 := NewGroup(expr)

pattern := BuildPattern(OperandJoin, BuildPattern(OperandProjection), BuildPattern(OperandSelection))
iter := NewExprIterFromGroupElem(g2.equivalents.Front(), pattern)
c.Assert(iter, NotNil)

count := 0
for ; iter.Matched(); iter.Next() {
count++
c.Assert(iter.group, IsNil)
c.Assert(iter.matched, Equals, true)
c.Assert(iter.operand, Equals, OperandJoin)
c.Assert(len(iter.children), Equals, 2)

c.Assert(iter.children[0].group, Equals, g0)
c.Assert(iter.children[0].matched, Equals, true)
c.Assert(iter.children[0].operand, Equals, OperandProjection)
c.Assert(len(iter.children[0].children), Equals, 0)

c.Assert(iter.children[1].group, Equals, g1)
c.Assert(iter.children[1].matched, Equals, true)
c.Assert(iter.children[1].operand, Equals, OperandSelection)
c.Assert(len(iter.children[1].children), Equals, 0)
}

c.Assert(count, Equals, 9)
}

func (s *testCascadesSuite) TestExprIterReset(c *C) {
g0 := NewGroup(NewGroupExpr(plannercore.LogicalProjection{}.Init(s.sctx)))
g0.Insert(NewGroupExpr(plannercore.LogicalLimit{}.Init(s.sctx)))
g0.Insert(NewGroupExpr(plannercore.LogicalProjection{}.Init(s.sctx)))
g0.Insert(NewGroupExpr(plannercore.LogicalLimit{}.Init(s.sctx)))
g0.Insert(NewGroupExpr(plannercore.LogicalProjection{}.Init(s.sctx)))

sel1 := NewGroupExpr(plannercore.LogicalSelection{}.Init(s.sctx))
sel2 := NewGroupExpr(plannercore.LogicalSelection{}.Init(s.sctx))
sel3 := NewGroupExpr(plannercore.LogicalSelection{}.Init(s.sctx))
g1 := NewGroup(sel1)
g1.Insert(NewGroupExpr(plannercore.LogicalLimit{}.Init(s.sctx)))
g1.Insert(sel2)
g1.Insert(NewGroupExpr(plannercore.LogicalLimit{}.Init(s.sctx)))
g1.Insert(sel3)

g2 := NewGroup(NewGroupExpr(plannercore.LogicalSelection{}.Init(s.sctx)))
g2.Insert(NewGroupExpr(plannercore.LogicalLimit{}.Init(s.sctx)))
g2.Insert(NewGroupExpr(plannercore.LogicalSelection{}.Init(s.sctx)))
g2.Insert(NewGroupExpr(plannercore.LogicalLimit{}.Init(s.sctx)))
g2.Insert(NewGroupExpr(plannercore.LogicalSelection{}.Init(s.sctx)))

// link join with group 0 and 1
expr := NewGroupExpr(plannercore.LogicalJoin{}.Init(s.sctx))
expr.children = append(expr.children, g0)
expr.children = append(expr.children, g1)
g3 := NewGroup(expr)

// link sel 1~3 with group 2
sel1.children = append(sel1.children, g2)
sel2.children = append(sel2.children, g2)
sel3.children = append(sel3.children, g2)

// create a pattern: join(proj, sel(limit))
lhsPattern := BuildPattern(OperandProjection)
rhsPattern := BuildPattern(OperandSelection, BuildPattern(OperandLimit))
pattern := BuildPattern(OperandJoin, lhsPattern, rhsPattern)

// create expression iterator for the pattern on join
iter := NewExprIterFromGroupElem(g3.equivalents.Front(), pattern)
c.Assert(iter, NotNil)

count := 0
for ; iter.Matched(); iter.Next() {
count++
c.Assert(iter.group, IsNil)
c.Assert(iter.matched, Equals, true)
c.Assert(iter.operand, Equals, OperandJoin)
c.Assert(len(iter.children), Equals, 2)

c.Assert(iter.children[0].group, Equals, g0)
c.Assert(iter.children[0].matched, Equals, true)
c.Assert(iter.children[0].operand, Equals, OperandProjection)
c.Assert(len(iter.children[0].children), Equals, 0)

c.Assert(iter.children[1].group, Equals, g1)
c.Assert(iter.children[1].matched, Equals, true)
c.Assert(iter.children[1].operand, Equals, OperandSelection)
c.Assert(len(iter.children[1].children), Equals, 1)

c.Assert(iter.children[1].children[0].group, Equals, g2)
c.Assert(iter.children[1].children[0].matched, Equals, true)
c.Assert(iter.children[1].children[0].operand, Equals, OperandLimit)
c.Assert(len(iter.children[1].children[0].children), Equals, 0)
}

c.Assert(count, Equals, 18)
}
Loading

0 comments on commit f06f3ca

Please sign in to comment.