Skip to content

Commit

Permalink
types: fix a bug in decimal multiplication (#7208) (#7212)
Browse files Browse the repository at this point in the history
  • Loading branch information
winkyao authored Jul 31, 2018
1 parent 71074b3 commit e05e983
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 6 deletions.
9 changes: 9 additions & 0 deletions expression/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3366,3 +3366,12 @@ func (s *testIntegrationSuite) TestPrefixIndex(c *C) {
res := tk.MustQuery("select * from t1 where name = '借款策略集_网页';")
res.Check(testkit.Rows("借款策略集_网页"))
}

func (s *testIntegrationSuite) TestDecimalMul(c *C) {
tk := testkit.NewTestKit(c, s.store)
tk.MustExec("USE test")
tk.MustExec("create table t(a decimal(38, 17));")
tk.MustExec("insert into t select 0.5999991229316*0.918755041726043;")
res := tk.MustQuery("select * from t;")
res.Check(testkit.Rows("0.55125221922461136"))
}
11 changes: 11 additions & 0 deletions tablecodec/tablecodec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"testing"
"time"

gofail "github.com/coreos/gofail/runtime"
. "github.com/pingcap/check"
"github.com/pingcap/tidb/mysql"
"github.com/pingcap/tidb/sessionctx/stmtctx"
Expand Down Expand Up @@ -278,6 +279,16 @@ func (s *testTableCodecSuite) TestCutKey(c *C) {
c.Assert(handleVal, DeepEquals, types.NewIntDatum(100))
}

func (s *testTableCodecSuite) TestDecodeBadDecical(c *C) {
gofail.Enable("github.com/pingcap/tidb/util/codec/errorInDecodeDecimal", `return(true)`)
defer gofail.Disable("github.com/pingcap/tidb/util/codec/errorInDecodeDecimal")
dec := types.NewDecFromStringForTest("0.111")
b := codec.EncodeDecimal(nil, dec, 0, 0)
// Expect no panic.
_, _, err := codec.DecodeOne(b)
c.Assert(err, NotNil)
}

func (s *testTableCodecSuite) TestIndexKey(c *C) {
tableID := int64(4)
indexID := int64(5)
Expand Down
22 changes: 20 additions & 2 deletions types/mydecimal.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,24 @@ func add(a, b, carry int32) (int32, int32) {
return sum, carry
}

// add2 adds a and b and carry, returns the sum and new carry.
// It is only used in DecimalMul.
func add2(a, b, carry int32) (int32, int32) {
sum := int64(a) + int64(b) + int64(carry)
if sum >= wordBase {
carry = 1
sum -= wordBase
} else {
carry = 0
}

if sum >= wordBase {
sum -= wordBase
carry++
}
return int32(sum), carry
}

// sub subtracts b and carry from a, returns the diff and new carry.
func sub(a, b, carry int32) (int32, int32) {
diff := a - b - carry
Expand Down Expand Up @@ -1777,7 +1795,7 @@ func DecimalMul(from1, from2, to *MyDecimal) error {
p := int64(from1.wordBuf[idx1]) * int64(from2.wordBuf[idx2])
hi = int32(p / wordBase)
lo = int32(p - int64(hi)*wordBase)
to.wordBuf[idxTo], carry = add(to.wordBuf[idxTo], lo, carry)
to.wordBuf[idxTo], carry = add2(to.wordBuf[idxTo], lo, carry)
carry += hi
idx2--
idxTo--
Expand All @@ -1786,7 +1804,7 @@ func DecimalMul(from1, from2, to *MyDecimal) error {
if idxTo < 0 {
return ErrOverflow
}
to.wordBuf[idxTo], carry = add(to.wordBuf[idxTo], 0, carry)
to.wordBuf[idxTo], carry = add2(to.wordBuf[idxTo], 0, carry)
}
for idxTo--; carry > 0; idxTo-- {
if idxTo < 0 {
Expand Down
2 changes: 2 additions & 0 deletions types/mydecimal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,8 @@ func (s *testMyDecimalSuite) TestMul(c *C) {
{"123", "0.01", "1.23", nil},
{"123", "0", "0", nil},
{"1" + strings.Repeat("0", 60), "1" + strings.Repeat("0", 60), "0", ErrOverflow},
{"0.5999991229316", "0.918755041726043", "0.5512522192246113614062276588", nil},
{"0.5999991229317", "0.918755041726042", "0.5512522192247026369112773314", nil},
}
for _, tt := range tests {
var a, b, product MyDecimal
Expand Down
10 changes: 6 additions & 4 deletions util/codec/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,10 +336,12 @@ func DecodeOne(b []byte) (remain []byte, d types.Datum, err error) {
case decimalFlag:
var dec *types.MyDecimal
b, dec, err = DecodeDecimal(b)
precision, frac := dec.PrecisionAndFrac()
d.SetMysqlDecimal(dec)
d.SetLength(precision)
d.SetFrac(frac)
if err == nil {
precision, frac := dec.PrecisionAndFrac()
d.SetMysqlDecimal(dec)
d.SetLength(precision)
d.SetFrac(frac)
}
case durationFlag:
var r int64
b, r, err = DecodeInt(b)
Expand Down
4 changes: 4 additions & 0 deletions util/codec/decimal.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ func EncodeDecimal(b []byte, dec *types.MyDecimal, precision, frac int) []byte {

// DecodeDecimal decodes bytes to decimal.
func DecodeDecimal(b []byte) ([]byte, *types.MyDecimal, error) {
// gofail: var errorInDecodeDecimal bool
// if errorInDecodeDecimal {
// return b, nil, errors.New("gofail error")
// }
if len(b) < 3 {
return b, nil, errors.New("insufficient bytes to decode value")
}
Expand Down

0 comments on commit e05e983

Please sign in to comment.