* *: fix issue#1071 and add tests * util: use RoundFloat in the ToBool and add tests.
This commit is contained in:
@ -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 {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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{
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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)
|
||||
|
||||
Reference in New Issue
Block a user