diff --git a/plan/plans/distinct.go b/plan/plans/distinct.go index b5acd0d77f..e30c41f8e6 100644 --- a/plan/plans/distinct.go +++ b/plan/plans/distinct.go @@ -24,6 +24,7 @@ import ( "github.com/pingcap/tidb/plan" "github.com/pingcap/tidb/util/format" "github.com/pingcap/tidb/util/types" + "github.com/reborndb/go/errors" ) var ( @@ -33,7 +34,9 @@ var ( // DistinctDefaultPlan e.g. SELECT distinct(id) FROM t; type DistinctDefaultPlan struct { *SelectList - Src plan.Plan + Src plan.Plan + rows []*plan.Row + cursor int } // Explain implements the plan.Plan Explain interface. @@ -95,10 +98,59 @@ func (r *DistinctDefaultPlan) Do(ctx context.Context, f plan.RowIterFunc) (err e // Next implements plan.Plan Next interface. func (r *DistinctDefaultPlan) Next(ctx context.Context) (row *plan.Row, err error) { + if r.rows == nil { + err = r.fetchAll(ctx) + if err != nil { + return nil, errors.Trace(err) + } + } + if r.cursor == len(r.rows) { + return + } + row = r.rows[r.cursor] + r.cursor++ return } +func (r *DistinctDefaultPlan) fetchAll(ctx context.Context) error { + t, err := memkv.CreateTemp(true) + if err != nil { + return errors.Trace(err) + } + defer func() { + if derr := t.Drop(); derr != nil && err == nil { + err = derr + } + }() + for { + row, err := r.Src.Next(ctx) + if row == nil || err != nil { + return errors.Trace(err) + } + var v []interface{} + // get distinct key + key := row.Data[0:r.HiddenFieldOffset] + v, err = t.Get(key) + if err != nil { + return errors.Trace(err) + } + + if len(v) == 0 { + // no group for key, save data for this group + r.rows = append(r.rows, row) + if err := t.Set(key, []interface{}{true}); err != nil { + return errors.Trace(err) + } + } + } +} + // Close implements plan.Plan Close interface. func (r *DistinctDefaultPlan) Close() error { - return nil + return r.Src.Close() +} + +// UseNext implements NextPlan interface +func (r *DistinctDefaultPlan) UseNext() bool { + return plan.UseNext(r.Src) } diff --git a/plan/plans/distinct_test.go b/plan/plans/distinct_test.go index 719e8e50ad..956de73dfa 100644 --- a/plan/plans/distinct_test.go +++ b/plan/plans/distinct_test.go @@ -11,34 +11,21 @@ // See the License for the specific language governing permissions and // limitations under the License. -package plans +package plans_test import ( "reflect" "testing" . "github.com/pingcap/check" - "github.com/pingcap/tidb/context" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/field" - "github.com/pingcap/tidb/plan" - "github.com/pingcap/tidb/util/format" + "github.com/pingcap/tidb/plan/plans" + "github.com/pingcap/tidb/rset/rsets" ) func TestT(t *testing.T) { TestingT(t) } -type testRowData struct { - id int64 - data []interface{} -} - -type testTablePlan struct { - rows []*testRowData - fields []string -} - var distinctTestData = []*testRowData{ {1, []interface{}{10, "hello"}}, {2, []interface{}{10, "hello"}}, @@ -47,55 +34,24 @@ var distinctTestData = []*testRowData{ {6, []interface{}{60, "hello"}}, } -func (p *testTablePlan) Do(ctx context.Context, f plan.RowIterFunc) error { - for _, d := range p.rows { - if more, err := f(d.id, d.data); !more || err != nil { - return err - } - } - return nil -} - -func (p *testTablePlan) Explain(w format.Formatter) {} - -func (p *testTablePlan) GetFields() []*field.ResultField { - var ret []*field.ResultField - for _, fn := range p.fields { - ret = append(ret, &field.ResultField{ - Name: fn, - }) - } - return ret -} - -func (p *testTablePlan) Filter(ctx context.Context, expr expression.Expression) (plan.Plan, bool, error) { - return p, false, nil -} - -func (p *testTablePlan) Next(ctx context.Context) (row *plan.Row, err error) { - return -} - -func (p *testTablePlan) Close() error { - return nil -} - type testDistinctSuit struct{} var _ = Suite(&testDistinctSuit{}) func (t *testDistinctSuit) TestDistinct(c *C) { - tblPlan := &testTablePlan{distinctTestData, []string{"id", "name"}} + tblPlan := &testTablePlan{distinctTestData, []string{"id", "name"}, 0} - p := DistinctDefaultPlan{ - SelectList: &SelectList{ + p := plans.DistinctDefaultPlan{ + SelectList: &plans.SelectList{ HiddenFieldOffset: len(tblPlan.GetFields()), }, Src: tblPlan, } - + rset := rsets.Recordset{ + Plan: &p, + } r := map[int][]interface{}{} - err := p.Do(nil, func(id interface{}, data []interface{}) (bool, error) { + err := rset.Do(func(data []interface{}) (bool, error) { r[data[0].(int)] = data return true, nil }) diff --git a/plan/plans/final_test.go b/plan/plans/final_test.go index db99dcbf8a..abb28d2d5b 100644 --- a/plan/plans/final_test.go +++ b/plan/plans/final_test.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package plans +package plans_test import ( . "github.com/pingcap/check" @@ -19,6 +19,7 @@ import ( "github.com/pingcap/tidb/field" "github.com/pingcap/tidb/model" mysql "github.com/pingcap/tidb/mysqldef" + "github.com/pingcap/tidb/plan/plans" "github.com/pingcap/tidb/util/charset" ) @@ -62,10 +63,10 @@ func (t *testFinalPlan) TestFinalPlan(c *C) { }, } - tblPlan := &testTablePlan{finalTestData, []string{"id", "name", "ok"}} + tblPlan := &testTablePlan{finalTestData, []string{"id", "name", "ok"}, 0} - p := &SelectFinalPlan{ - SelectList: &SelectList{ + p := &plans.SelectFinalPlan{ + SelectList: &plans.SelectList{ HiddenFieldOffset: len(tblPlan.GetFields()), ResultFields: []*field.ResultField{ field.ColToResultField(col1, "t"), diff --git a/plan/plans/groupby_test.go b/plan/plans/groupby_test.go index c65d931cd4..41a041884c 100644 --- a/plan/plans/groupby_test.go +++ b/plan/plans/groupby_test.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package plans +package plans_test import ( . "github.com/pingcap/check" @@ -19,6 +19,7 @@ import ( "github.com/pingcap/tidb/expression/expressions" "github.com/pingcap/tidb/field" "github.com/pingcap/tidb/model" + "github.com/pingcap/tidb/plan/plans" ) type testGroupBySuite struct{} @@ -34,9 +35,9 @@ var groupByTestData = []*testRowData{ } func (t *testGroupBySuite) TestGroupBy(c *C) { - tblPlan := &testTablePlan{groupByTestData, []string{"id", "name"}} + tblPlan := &testTablePlan{groupByTestData, []string{"id", "name"}, 0} // test multiple fields - sl := &SelectList{ + sl := &plans.SelectList{ Fields: []*field.Field{ { Expr: &expressions.Ident{ @@ -62,7 +63,7 @@ func (t *testGroupBySuite) TestGroupBy(c *C) { AggFields: map[int]struct{}{2: {}}, } - groupbyPlan := &GroupByDefaultPlan{ + groupbyPlan := &plans.GroupByDefaultPlan{ SelectList: sl, Src: tblPlan, By: []expression.Expression{ diff --git a/plan/plans/having_test.go b/plan/plans/having_test.go index 476266fd77..2928d20428 100644 --- a/plan/plans/having_test.go +++ b/plan/plans/having_test.go @@ -11,13 +11,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -package plans +package plans_test import ( . "github.com/pingcap/check" "github.com/pingcap/tidb/expression/expressions" "github.com/pingcap/tidb/model" "github.com/pingcap/tidb/parser/opcode" + "github.com/pingcap/tidb/plan/plans" ) type testHavingPlan struct{} @@ -33,8 +34,8 @@ var havingTestData = []*testRowData{ } func (t *testHavingPlan) TestHaving(c *C) { - tblPlan := &testTablePlan{groupByTestData, []string{"id", "name"}} - havingPlan := &HavingPlan{ + tblPlan := &testTablePlan{groupByTestData, []string{"id", "name"}, 0} + havingPlan := &plans.HavingPlan{ Src: tblPlan, Expr: &expressions.BinaryOperation{ Op: opcode.GE, diff --git a/plan/plans/join_test.go b/plan/plans/join_test.go index ed7db68750..f377d06f8b 100644 --- a/plan/plans/join_test.go +++ b/plan/plans/join_test.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package plans +package plans_test import ( . "github.com/pingcap/check" @@ -21,6 +21,7 @@ import ( "github.com/pingcap/tidb/model" mysql "github.com/pingcap/tidb/mysqldef" "github.com/pingcap/tidb/parser/opcode" + "github.com/pingcap/tidb/plan/plans" "github.com/pingcap/tidb/util/types" ) @@ -68,10 +69,10 @@ func (s *testJoinSuit) TestJoin(c *C) { {6, []interface{}{60, "60"}}, } - tblPlan1 := &testTablePlan{testData1, []string{"id", "name"}} - tblPlan2 := &testTablePlan{testData2, []string{"id", "name"}} + tblPlan1 := &testTablePlan{testData1, []string{"id", "name"}, 0} + tblPlan2 := &testTablePlan{testData2, []string{"id", "name"}, 0} - joinPlan := &JoinPlan{ + joinPlan := &plans.JoinPlan{ Left: tblPlan1, Right: tblPlan2, Type: "CROSS", @@ -83,7 +84,7 @@ func (s *testJoinSuit) TestJoin(c *C) { return true, nil }) - joinPlan = &JoinPlan{ + joinPlan = &plans.JoinPlan{ Left: tblPlan1, Right: tblPlan2, Type: "LEFT", @@ -95,7 +96,7 @@ func (s *testJoinSuit) TestJoin(c *C) { return true, nil }) - joinPlan = &JoinPlan{ + joinPlan = &plans.JoinPlan{ Left: tblPlan1, Right: tblPlan2, Type: "RIGHT", @@ -120,7 +121,7 @@ func (s *testJoinSuit) TestJoin(c *C) { return true, nil }) - joinPlan = &JoinPlan{ + joinPlan = &plans.JoinPlan{ Left: tblPlan1, Right: tblPlan2, Type: "FULL", diff --git a/plan/plans/union_test.go b/plan/plans/union_test.go index a43c22c54a..cf3a476f7d 100644 --- a/plan/plans/union_test.go +++ b/plan/plans/union_test.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package plans +package plans_test import ( . "github.com/pingcap/check" @@ -20,6 +20,7 @@ import ( "github.com/pingcap/tidb/model" mysql "github.com/pingcap/tidb/mysqldef" "github.com/pingcap/tidb/plan" + "github.com/pingcap/tidb/plan/plans" "github.com/pingcap/tidb/util/types" ) @@ -46,8 +47,8 @@ var _ = Suite(&testUnionSuit{ }) func (t *testUnionSuit) TestUnion(c *C) { - tblPlan := &testTablePlan{t.data, []string{"id", "name"}} - tblPlan2 := &testTablePlan{t.data2, []string{"id", "name"}} + tblPlan := &testTablePlan{t.data, []string{"id", "name"}, 0} + tblPlan2 := &testTablePlan{t.data2, []string{"id", "name"}, 0} cols := []*column.Col{ { ColumnInfo: model.ColumnInfo{ @@ -69,7 +70,7 @@ func (t *testUnionSuit) TestUnion(c *C) { }, } - pln := &UnionPlan{ + pln := &plans.UnionPlan{ Srcs: []plan.Plan{ tblPlan, tblPlan2,