*: Fix issue #1071 (#1088)

* *: fix issue#1071 and add tests

* util: use RoundFloat in the ToBool and add tests.
This commit is contained in:
zimulala
2016-04-14 16:56:24 +08:00
parent 2baa40f59b
commit bfa3ed7a2d
9 changed files with 129 additions and 21 deletions

View File

@ -95,6 +95,8 @@ func (b *executorBuilder) build(p plan.Plan) Executor {
return b.buildSimple(v)
case *plan.Sort:
return b.buildSort(v)
case *plan.TableDual:
return b.buildTableDual(v)
case *plan.TableScan:
return b.buildTableScan(v)
case *plan.Union:
@ -118,6 +120,11 @@ func (b *executorBuilder) buildFilter(src Executor, conditions []ast.ExprNode) E
}
}
func (b *executorBuilder) buildTableDual(v *plan.TableDual) Executor {
e := &TableDualExec{fields: v.Fields()}
return b.buildFilter(e, v.FilterConditions)
}
func (b *executorBuilder) buildTableScan(v *plan.TableScan) Executor {
txn, err := b.ctx.GetTxn(false)
if err != nil {

View File

@ -46,6 +46,7 @@ var (
_ Executor = &SelectLockExec{}
_ Executor = &ShowDDLExec{}
_ Executor = &SortExec{}
_ Executor = &TableDualExec{}
_ Executor = &TableScanExec{}
)
@ -103,7 +104,7 @@ func (e *ShowDDLExec) Fields() []*ast.ResultField {
return e.fields
}
// Next implements Execution Next interface.
// Next implements Executor Next interface.
func (e *ShowDDLExec) Next() (*Row, error) {
if e.done {
return nil, nil
@ -173,7 +174,7 @@ func (e *CheckTableExec) Fields() []*ast.ResultField {
return nil
}
// Next implements Execution Next interface.
// Next implements Executor Next interface.
func (e *CheckTableExec) Next() (*Row, error) {
if e.done {
return nil, nil
@ -208,6 +209,31 @@ func (e *CheckTableExec) Close() error {
return nil
}
// TableDualExec represents a dual table executor.
type TableDualExec struct {
fields []*ast.ResultField
executed bool
}
// Fields implements Executor Fields interface.
func (e *TableDualExec) Fields() []*ast.ResultField {
return e.fields
}
// Next implements Executor Next interface.
func (e *TableDualExec) Next() (*Row, error) {
if e.executed {
return nil, nil
}
e.executed = true
return &Row{}, nil
}
// Close implements plan.Plan Close interface.
func (e *TableDualExec) Close() error {
return nil
}
// TableScanExec represents a table scan executor.
type TableScanExec struct {
t table.Table
@ -224,7 +250,7 @@ func (e *TableScanExec) Fields() []*ast.ResultField {
return e.fields
}
// Next implements Execution Next interface.
// Next implements Executor Next interface.
func (e *TableScanExec) Next() (*Row, error) {
for {
if e.cursor >= len(e.ranges) {

View File

@ -231,6 +231,9 @@ func (b *planBuilder) buildSelect(sel *ast.SelectStmt) Plan {
return nil
}
} else {
if sel.Where != nil {
p = b.buildTableDual(sel)
}
if hasAgg {
p = b.buildAggregate(p, aggFuncs, nil)
}
@ -324,6 +327,13 @@ func (b *planBuilder) buildAllAccessMethodsPlan(path *joinPath) []Plan {
return candidates
}
func (b *planBuilder) buildTableDual(sel *ast.SelectStmt) Plan {
dual := &TableDual{FilterConditions: splitWhere(sel.Where)}
ret := ast.ResultField{}
dual.SetFields([]*ast.ResultField{&ret})
return dual
}
func (b *planBuilder) buildTableScanPlan(path *joinPath) Plan {
tn := path.table
p := &TableScan{

View File

@ -26,6 +26,21 @@ type TableRange struct {
HighVal int64
}
// TableDual represents a dual table plan.
type TableDual struct {
basePlan
HasAgg bool
// FilterConditions can be used to filter result.
FilterConditions []ast.ExprNode
}
// Accept implements Plan Accept interface.
func (p *TableDual) Accept(v Visitor) (Plan, bool) {
np, _ := v.Enter(p)
return v.Leave(np)
}
// TableScan represents a table scan plan.
type TableScan struct {
basePlan

View File

@ -814,6 +814,56 @@ func (s *testSessionSuite) TestSelect(c *C) {
c.Assert(err, IsNil)
match(c, row.Data, 1, 2)
// Testcase For https://github.com/pingcap/tidb/issues/1071
r = mustExecSQL(c, se, `select 1 from dual where "0.1"`)
row, err = r.Next()
c.Assert(err, IsNil)
c.Assert(row, IsNil)
r = mustExecSQL(c, se, "select 1 from dual where 0.8")
row, err = r.Next()
c.Assert(err, IsNil)
match(c, row.Data, 1)
r = mustExecSQL(c, se, "select 1, count(*) from dual where 0.1")
row, err = r.Next()
c.Assert(err, IsNil)
match(c, row.Data, 1, 0)
r = mustExecSQL(c, se, "select count(*), 1 from dual where 0.8")
row, err = r.Next()
c.Assert(err, IsNil)
match(c, row.Data, 1, 1)
r = mustExecSQL(c, se, "select 1, 2 from dual where 0.1")
row, err = r.Next()
c.Assert(err, IsNil)
c.Assert(row, IsNil)
mustExecSQL(c, se, "create table if not exists t2 (c1 int, c2 int)")
mustExecSQL(c, se, "insert into t2 (c1, c2) values(1, 1), (2, 2), (3, 3)")
r = mustExecSQL(c, se, "select 1 from t2 where 0.1")
row, err = r.Next()
c.Assert(err, IsNil)
c.Assert(row, IsNil)
r = mustExecSQL(c, se, "select 1 from t2 where 0.9")
row, err = r.Next()
c.Assert(err, IsNil)
match(c, row.Data, 1)
row, err = r.Next()
c.Assert(err, IsNil)
match(c, row.Data, 1)
row, err = r.Next()
c.Assert(err, IsNil)
match(c, row.Data, 1)
r = mustExecSQL(c, se, "select sum(c1) from t2 where 0.1")
row, err = r.Next()
c.Assert(err, IsNil)
match(c, row.Data, nil)
r = mustExecSQL(c, se, "select sum(c1), c2 from t2 where 0.1")
row, err = r.Next()
c.Assert(err, IsNil)
match(c, row.Data, nil, nil)
r = mustExecSQL(c, se, "select 1+2, count(c1) from t2 where 0.1")
row, err = r.Next()
c.Assert(err, IsNil)
match(c, row.Data, 3, 0)
r = mustExecSQL(c, se, "select 1, 2 from dual where not exists (select * from t where c1=2)")
row, err = r.Next()
c.Assert(err, IsNil)

View File

@ -387,9 +387,9 @@ func ToBool(value interface{}) (int64, error) {
case uint64:
isZero = (v == 0)
case float32:
isZero = (v == 0)
isZero = (RoundFloat(float64(v)) == 0)
case float64:
isZero = (v == 0)
isZero = (RoundFloat(v) == 0)
case string:
if len(v) == 0 {
isZero = true
@ -416,7 +416,7 @@ func ToBool(value interface{}) (int64, error) {
isZero = (v.Duration == 0)
case mysql.Decimal:
vv, _ := v.Float64()
isZero = (vv == 0)
isZero = (RoundFloat(vv) == 0)
case mysql.Hex:
isZero = (v.ToNumber() == 0)
case mysql.Bit:

View File

@ -380,12 +380,12 @@ func (s *testTypeConvertSuite) TestConvertToBool(c *C) {
testToBool(c, int(0), 0)
testToBool(c, int64(0), 0)
testToBool(c, uint64(0), 0)
testToBool(c, float32(0), 0)
testToBool(c, float64(0), 0)
testToBool(c, float32(0.1), 0)
testToBool(c, float64(0.1), 0)
testToBool(c, "", 0)
testToBool(c, "0", 0)
testToBool(c, "0.1", 0)
testToBool(c, []byte{}, 0)
testToBool(c, []byte("0"), 0)
testToBool(c, []byte("0.1"), 0)
testToBool(c, mysql.Hex{Value: 0}, 0)
testToBool(c, mysql.Bit{Value: 0, Width: 8}, 0)
testToBool(c, mysql.Enum{Name: "a", Value: 1}, 1)
@ -401,9 +401,9 @@ func (s *testTypeConvertSuite) TestConvertToBool(c *C) {
ft := NewFieldType(mysql.TypeNewDecimal)
ft.Decimal = 5
v, err := Convert(3.1415926, ft)
v, err := Convert(0.1415926, ft)
c.Assert(err, IsNil)
testToBool(c, v, 1)
testToBool(c, v, 0)
_, err = ToBool(&invalidMockType{})
c.Assert(err, NotNil)

View File

@ -1065,9 +1065,9 @@ func (d *Datum) ToBool() (int64, error) {
case KindUint64:
isZero = (d.GetUint64() == 0)
case KindFloat32:
isZero = (d.GetFloat32() == 0)
isZero = (RoundFloat(d.GetFloat64()) == 0)
case KindFloat64:
isZero = (d.GetFloat64() == 0)
isZero = (RoundFloat(d.GetFloat64()) == 0)
case KindString:
s := d.GetString()
if len(s) == 0 {
@ -1095,7 +1095,7 @@ func (d *Datum) ToBool() (int64, error) {
isZero = (d.GetMysqlDuration().Duration == 0)
case KindMysqlDecimal:
v, _ := d.GetMysqlDecimal().Float64()
isZero = (v == 0)
isZero = (RoundFloat(v) == 0)
case KindMysqlHex:
isZero = (d.GetMysqlHex().ToNumber() == 0)
case KindMysqlBit:

View File

@ -52,12 +52,12 @@ func (ts *testDatumSuite) TestToBool(c *C) {
testDatumToBool(c, int(0), 0)
testDatumToBool(c, int64(0), 0)
testDatumToBool(c, uint64(0), 0)
testDatumToBool(c, float32(0), 0)
testDatumToBool(c, float64(0), 0)
testDatumToBool(c, float32(0.1), 0)
testDatumToBool(c, float64(0.1), 0)
testDatumToBool(c, "", 0)
testDatumToBool(c, "0", 0)
testDatumToBool(c, "0.1", 0)
testDatumToBool(c, []byte{}, 0)
testDatumToBool(c, []byte("0"), 0)
testDatumToBool(c, []byte("0.1"), 0)
testDatumToBool(c, mysql.Hex{Value: 0}, 0)
testDatumToBool(c, mysql.Bit{Value: 0, Width: 8}, 0)
testDatumToBool(c, mysql.Enum{Name: "a", Value: 1}, 1)
@ -73,9 +73,9 @@ func (ts *testDatumSuite) TestToBool(c *C) {
ft := NewFieldType(mysql.TypeNewDecimal)
ft.Decimal = 5
v, err := Convert(3.1415926, ft)
v, err := Convert(0.1415926, ft)
c.Assert(err, IsNil)
testDatumToBool(c, v, 1)
testDatumToBool(c, v, 0)
d := NewDatum(&invalidMockType{})
_, err = d.ToBool()
c.Assert(err, NotNil)