From 0980447051fb0d07ce7e7718f491a5e21ba1c8ec Mon Sep 17 00:00:00 2001 From: Jian Zhang Date: Tue, 16 Oct 2018 17:21:53 +0800 Subject: [PATCH 1/3] planner: support the Group and GroupExpr for the cascades planner --- planner/cascades/group.go | 72 +++++++++++++++++++++++ planner/cascades/group_expr.go | 53 +++++++++++++++++ planner/cascades/group_expr_test.go | 35 ++++++++++++ planner/cascades/group_test.go | 89 +++++++++++++++++++++++++++++ 4 files changed, 249 insertions(+) create mode 100644 planner/cascades/group.go create mode 100644 planner/cascades/group_expr.go create mode 100644 planner/cascades/group_expr_test.go create mode 100644 planner/cascades/group_test.go diff --git a/planner/cascades/group.go b/planner/cascades/group.go new file mode 100644 index 0000000000000..24f7cbb569977 --- /dev/null +++ b/planner/cascades/group.go @@ -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 +} diff --git a/planner/cascades/group_expr.go b/planner/cascades/group_expr.go new file mode 100644 index 0000000000000..757ea33ff9315 --- /dev/null +++ b/planner/cascades/group_expr.go @@ -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 +} diff --git a/planner/cascades/group_expr_test.go b/planner/cascades/group_expr_test.go new file mode 100644 index 0000000000000..ca111959ee282 --- /dev/null +++ b/planner/cascades/group_expr_test.go @@ -0,0 +1,35 @@ +// 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) TestNewGroupExpr(c *C) { + p := &plannercore.LogicalLimit{} + expr := NewGroupExpr(p) + c.Assert(expr.exprNode, Equals, p) + c.Assert(expr.children, IsNil) + c.Assert(expr.explored, IsFalse) +} + +func (s *testCascadesSuite) TestGroupExprFingerprint(c *C) { + p := &plannercore.LogicalLimit{} + expr := NewGroupExpr(p) + + // we haven't set the id of the created LogicalLimit, so the result is 0. + c.Assert(expr.FingerPrint(), Equals, "0") +} diff --git a/planner/cascades/group_test.go b/planner/cascades/group_test.go new file mode 100644 index 0000000000000..7e7722af53b3f --- /dev/null +++ b/planner/cascades/group_test.go @@ -0,0 +1,89 @@ +// 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" + "testing" + + . "github.com/pingcap/check" + plannercore "github.com/pingcap/tidb/planner/core" + "github.com/pingcap/tidb/sessionctx" + "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/util/testleak" +) + +func TestT(t *testing.T) { + CustomVerboseFlag = true + TestingT(t) +} + +var _ = Suite(&testCascadesSuite{}) + +type testCascadesSuite struct { + sctx sessionctx.Context +} + +func (s *testCascadesSuite) SetUpSuite(c *C) { + testleak.BeforeTest() + s.sctx = mock.NewContext() +} + +func (s *testCascadesSuite) TearDownSuite(c *C) { + testleak.AfterTest(c)() +} + +func (s *testCascadesSuite) TestNewGroup(c *C) { + p := &plannercore.LogicalLimit{} + expr := NewGroupExpr(p) + g := NewGroup(expr) + + c.Assert(g.equivalents.Len(), Equals, 1) + c.Assert(g.equivalents.Front().Value.(*GroupExpr), Equals, expr) + c.Assert(len(g.fingerprints), Equals, 1) + c.Assert(g.explored, IsFalse) +} + +func (s *testCascadesSuite) TestGroupInsert(c *C) { + p := &plannercore.LogicalLimit{} + expr := NewGroupExpr(p) + g := NewGroup(expr) + c.Assert(g.Insert(expr), IsFalse) + expr.selfFingerprint = "1" + c.Assert(g.Insert(expr), IsTrue) +} + +func (s *testCascadesSuite) TestGroupDelete(c *C) { + p := &plannercore.LogicalLimit{} + expr := NewGroupExpr(p) + g := NewGroup(expr) + c.Assert(g.equivalents.Len(), Equals, 1) + + g.Delete(expr) + c.Assert(g.equivalents.Len(), Equals, 0) + + g.Delete(expr) + c.Assert(g.equivalents.Len(), Equals, 0) +} + +func (s *testCascadesSuite) TestGroupExists(c *C) { + p := &plannercore.LogicalLimit{} + expr := NewGroupExpr(p) + g := NewGroup(expr) + c.Assert(g.Exists(expr), IsTrue) + + g.Delete(expr) + c.Assert(g.Exists(expr), IsFalse) +} From 86729afdfba4083e30958d0611f286ca882468e2 Mon Sep 17 00:00:00 2001 From: Jian Zhang Date: Tue, 16 Oct 2018 18:08:50 +0800 Subject: [PATCH 2/3] address comment --- planner/cascades/group_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/planner/cascades/group_test.go b/planner/cascades/group_test.go index 7e7722af53b3f..a5eab747aee27 100644 --- a/planner/cascades/group_test.go +++ b/planner/cascades/group_test.go @@ -14,8 +14,6 @@ package cascades import ( - // "container/list" - //"fmt" "testing" . "github.com/pingcap/check" From 9d6cd5036675ed3b9d3307811c01342688be20ee Mon Sep 17 00:00:00 2001 From: Jian Zhang Date: Thu, 18 Oct 2018 15:08:04 +0800 Subject: [PATCH 3/3] address comment --- planner/cascades/group.go | 2 +- planner/cascades/group_expr.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/planner/cascades/group.go b/planner/cascades/group.go index 24f7cbb569977..c044ec78fea1a 100644 --- a/planner/cascades/group.go +++ b/planner/cascades/group.go @@ -46,7 +46,7 @@ func (g *Group) FingerPrint() string { return g.selfFingerprint } -// Insert a nonexistent group exxpression. +// Insert a nonexistent group expression. func (g *Group) Insert(e *GroupExpr) bool { if g.Exists(e) { return false diff --git a/planner/cascades/group_expr.go b/planner/cascades/group_expr.go index 757ea33ff9315..cda5bd08927dd 100644 --- a/planner/cascades/group_expr.go +++ b/planner/cascades/group_expr.go @@ -41,7 +41,7 @@ func NewGroupExpr(node plannercore.LogicalPlan) *GroupExpr { } } -// FingerPrint get the unique fingerprint of the group expression. +// FingerPrint gets the unique fingerprint of the group expression. func (e *GroupExpr) FingerPrint() string { if e.selfFingerprint == "" { e.selfFingerprint = fmt.Sprintf("%v", e.exprNode.ID())