plan: implement DistinctDefaultPlan and modified some tests
This commit is contained in:
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
})
|
||||
|
||||
@ -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"),
|
||||
|
||||
@ -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{
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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,
|
||||
|
||||
Reference in New Issue
Block a user