From a49eeb40fb5ec08c979a82ea113d7762f9d2b5da Mon Sep 17 00:00:00 2001 From: Jian Zhang Date: Fri, 26 Oct 2018 15:55:07 +0800 Subject: [PATCH] add ut --- planner/cascades/expr_iterator.go | 33 +++++--- planner/cascades/expr_iterator_test.go | 102 +++++++++++++++++++++++++ planner/cascades/optimize.go | 9 ++- planner/cascades/pattern_test.go | 86 +++++++++++++++++++++ 4 files changed, 218 insertions(+), 12 deletions(-) create mode 100644 planner/cascades/expr_iterator_test.go create mode 100644 planner/cascades/pattern_test.go diff --git a/planner/cascades/expr_iterator.go b/planner/cascades/expr_iterator.go index 2d5d577df138c..514bf93106e15 100644 --- a/planner/cascades/expr_iterator.go +++ b/planner/cascades/expr_iterator.go @@ -60,8 +60,8 @@ func (iter *ExprIter) Next() (found bool) { } // Otherwise, iterate itself to find more matched equivalent expressions. - for iter.element.Next(); iter.element != nil; iter.element.Next() { - expr := iter.element.Value.(*GroupExpr) + for elem := iter.element.Next(); elem != nil; elem = elem.Next() { + expr := elem.Value.(*GroupExpr) exprOperand := GetOperand(expr.exprNode) if !iter.operand.match(exprOperand) { @@ -85,6 +85,7 @@ func (iter *ExprIter) Next() (found bool) { } if allMatched { + iter.element = elem return true } } @@ -100,18 +101,32 @@ func (iter *ExprIter) Matched() bool { // Reset resets the iterator to the first matched group expression. func (iter *ExprIter) Reset() (findMatch bool) { iter.element = iter.group.GetFirstElem(iter.operand) - return iter.element != nil + iter.matched = iter.element != nil + return iter.matched +} + +// 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 { +// 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]) + childIter := newExprIterFromGroup(expr.children[i], p.children[i]) if childIter == nil { return nil } @@ -120,14 +135,14 @@ func NewExprIterFromGroupExpr(expr *GroupExpr, p *Pattern) *ExprIter { return iter } -// NewExprIterFromGroup creates the iterator on the group. -func NewExprIterFromGroup(g *Group, p *Pattern) *ExprIter { +// 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) + iter := newExprIterFromGroupExpr(expr, p) if iter != nil { iter.group, iter.element = g, elem return iter diff --git a/planner/cascades/expr_iterator_test.go b/planner/cascades/expr_iterator_test.go new file mode 100644 index 0000000000000..76d88ca07300a --- /dev/null +++ b/planner/cascades/expr_iterator_test.go @@ -0,0 +1,102 @@ +// 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[0].children), Equals, 0) + } + + c.Assert(count, Equals, 9) +} diff --git a/planner/cascades/optimize.go b/planner/cascades/optimize.go index cfc27aeb7c65d..9316c8f9ccbdc 100644 --- a/planner/cascades/optimize.go +++ b/planner/cascades/optimize.go @@ -14,6 +14,8 @@ package cascades import ( + "container/list" + plannercore "github.com/pingcap/tidb/planner/core" "github.com/pingcap/tidb/sessionctx" "github.com/pkg/errors" @@ -73,7 +75,7 @@ func exploreGroup(g *Group) error { curExpr.explored = curExpr.explored && childGroup.explored } - eraseCur, err := findMoreEquiv(curExpr, g) + eraseCur, err := findMoreEquiv(g, elem) if err != nil { return err } @@ -87,7 +89,8 @@ func exploreGroup(g *Group) error { } // findMoreEquiv finds and applies the matched transformation rules. -func findMoreEquiv(expr *GroupExpr, g *Group) (eraseCur bool, err error) { +func findMoreEquiv(g *Group, elem *list.Element) (eraseCur bool, err error) { + expr := elem.Value.(*GroupExpr) for _, rule := range GetTransformationRules(expr.exprNode) { pattern := rule.GetPattern() if !pattern.operand.match(GetOperand(expr.exprNode)) { @@ -95,7 +98,7 @@ func findMoreEquiv(expr *GroupExpr, g *Group) (eraseCur bool, err error) { } // Create a binding of the current group expression and the pattern of // the transformation rule to enumerate all the possible expressions. - iter := NewExprIterFromGroupExpr(expr, pattern) + iter := NewExprIterFromGroupElem(elem, pattern) for ; iter != nil && iter.Matched(); iter.Next() { if !rule.Match(iter) { continue diff --git a/planner/cascades/pattern_test.go b/planner/cascades/pattern_test.go new file mode 100644 index 0000000000000..fb5bbbb50e043 --- /dev/null +++ b/planner/cascades/pattern_test.go @@ -0,0 +1,86 @@ +// 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) TestGetOperand(c *C) { + c.Assert(GetOperand(&plannercore.LogicalJoin{}), Equals, OperandJoin) + c.Assert(GetOperand(&plannercore.LogicalAggregation{}), Equals, OperandAggregation) + c.Assert(GetOperand(&plannercore.LogicalProjection{}), Equals, OperandProjection) + c.Assert(GetOperand(&plannercore.LogicalSelection{}), Equals, OperandSelection) + c.Assert(GetOperand(&plannercore.LogicalApply{}), Equals, OperandApply) + c.Assert(GetOperand(&plannercore.LogicalMaxOneRow{}), Equals, OperandMaxOneRow) + c.Assert(GetOperand(&plannercore.LogicalTableDual{}), Equals, OperandTableDual) + c.Assert(GetOperand(&plannercore.DataSource{}), Equals, OperandDataSource) + c.Assert(GetOperand(&plannercore.LogicalUnionScan{}), Equals, OperandUnionScan) + c.Assert(GetOperand(&plannercore.LogicalUnionAll{}), Equals, OperandUnionAll) + c.Assert(GetOperand(&plannercore.LogicalSort{}), Equals, OperandSort) + c.Assert(GetOperand(&plannercore.LogicalTopN{}), Equals, OperandTopN) + c.Assert(GetOperand(&plannercore.LogicalLock{}), Equals, OperandLock) + c.Assert(GetOperand(&plannercore.LogicalLimit{}), Equals, OperandLimit) +} + +func (s *testCascadesSuite) TestOperandMatch(c *C) { + c.Assert(OperandAny.match(OperandLimit), IsTrue) + c.Assert(OperandAny.match(OperandSelection), IsTrue) + c.Assert(OperandAny.match(OperandJoin), IsTrue) + c.Assert(OperandAny.match(OperandMaxOneRow), IsTrue) + c.Assert(OperandAny.match(OperandAny), IsTrue) + + c.Assert(OperandLimit.match(OperandAny), IsTrue) + c.Assert(OperandSelection.match(OperandAny), IsTrue) + c.Assert(OperandJoin.match(OperandAny), IsTrue) + c.Assert(OperandMaxOneRow.match(OperandAny), IsTrue) + c.Assert(OperandAny.match(OperandAny), IsTrue) + + c.Assert(OperandLimit.match(OperandLimit), IsTrue) + c.Assert(OperandSelection.match(OperandSelection), IsTrue) + c.Assert(OperandJoin.match(OperandJoin), IsTrue) + c.Assert(OperandMaxOneRow.match(OperandMaxOneRow), IsTrue) + c.Assert(OperandAny.match(OperandAny), IsTrue) + + c.Assert(OperandLimit.match(OperandSelection), IsFalse) + c.Assert(OperandLimit.match(OperandJoin), IsFalse) + c.Assert(OperandLimit.match(OperandMaxOneRow), IsFalse) +} + +func (s *testCascadesSuite) TestNewPattern(c *C) { + p := NewPattern(OperandAny) + c.Assert(p.operand, Equals, OperandAny) + c.Assert(p.children, IsNil) + + p = NewPattern(OperandJoin) + c.Assert(p.operand, Equals, OperandJoin) + c.Assert(p.children, IsNil) +} + +func (s *testCascadesSuite) TestPatternSetChildren(c *C) { + p := NewPattern(OperandAny) + p.SetChildren(NewPattern(OperandLimit)) + c.Assert(len(p.children), Equals, 1) + c.Assert(p.children[0].operand, Equals, OperandLimit) + c.Assert(p.children[0].children, IsNil) + + p = NewPattern(OperandJoin) + p.SetChildren(NewPattern(OperandProjection), NewPattern(OperandSelection)) + c.Assert(len(p.children), Equals, 2) + c.Assert(p.children[0].operand, Equals, OperandProjection) + c.Assert(p.children[0].children, IsNil) + c.Assert(p.children[1].operand, Equals, OperandSelection) + c.Assert(p.children[1].children, IsNil) +}