-
Notifications
You must be signed in to change notification settings - Fork 5.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
planner: support the transformation framework of the cascades planner (…
- Loading branch information
Showing
4 changed files
with
467 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
Oops, something went wrong.