Skip to content

Commit

Permalink
expression: fix extract bug when argument is a negative duration (#27318
Browse files Browse the repository at this point in the history
) (#27369)
  • Loading branch information
ti-srebot authored Sep 10, 2021
1 parent 993cadf commit f529696
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 39 deletions.
13 changes: 13 additions & 0 deletions expression/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8284,6 +8284,19 @@ func (s *testIntegrationSuite) TestJiraSetInnoDBDefaultRowFormat(c *C) {
tk.MustQuery("SHOW VARIABLES LIKE 'innodb_large_prefix'").Check(testkit.Rows("innodb_large_prefix ON"))
}

func (s *testIntegrationSuite) TestIssue27236(c *C) {
tk := testkit.NewTestKit(c, s.store)
tk.MustExec("use test;")
row := tk.MustQuery(`select extract(hour_second from "-838:59:59.00");`)
row.Check(testkit.Rows("-8385959"))

tk.MustExec(`drop table if exists t`)
tk.MustExec(`create table t(c1 varchar(100));`)
tk.MustExec(`insert into t values('-838:59:59.00'), ('700:59:59.00');`)
row = tk.MustQuery(`select extract(hour_second from c1) from t order by c1;`)
row.Check(testkit.Rows("-8385959", "7005959"))
}

func (s *testIntegrationSuite) TestConstPropNullFunctions(c *C) {
tk := testkit.NewTestKit(c, s.store)
tk.MustExec("use test")
Expand Down
34 changes: 19 additions & 15 deletions types/time.go
Original file line number Diff line number Diff line change
Expand Up @@ -2182,39 +2182,43 @@ func ExtractDatetimeNum(t *Time, unit string) (int64, error) {
}

// ExtractDurationNum extracts duration value number from duration unit and format.
func ExtractDurationNum(d *Duration, unit string) (int64, error) {
func ExtractDurationNum(d *Duration, unit string) (res int64, err error) {
switch strings.ToUpper(unit) {
case "MICROSECOND":
return int64(d.MicroSecond()), nil
res = int64(d.MicroSecond())
case "SECOND":
return int64(d.Second()), nil
res = int64(d.Second())
case "MINUTE":
return int64(d.Minute()), nil
res = int64(d.Minute())
case "HOUR":
return int64(d.Hour()), nil
res = int64(d.Hour())
case "SECOND_MICROSECOND":
return int64(d.Second())*1000000 + int64(d.MicroSecond()), nil
res = int64(d.Second())*1000000 + int64(d.MicroSecond())
case "MINUTE_MICROSECOND":
return int64(d.Minute())*100000000 + int64(d.Second())*1000000 + int64(d.MicroSecond()), nil
res = int64(d.Minute())*100000000 + int64(d.Second())*1000000 + int64(d.MicroSecond())
case "MINUTE_SECOND":
return int64(d.Minute()*100 + d.Second()), nil
res = int64(d.Minute()*100 + d.Second())
case "HOUR_MICROSECOND":
return int64(d.Hour())*10000000000 + int64(d.Minute())*100000000 + int64(d.Second())*1000000 + int64(d.MicroSecond()), nil
res = int64(d.Hour())*10000000000 + int64(d.Minute())*100000000 + int64(d.Second())*1000000 + int64(d.MicroSecond())
case "HOUR_SECOND":
return int64(d.Hour())*10000 + int64(d.Minute())*100 + int64(d.Second()), nil
res = int64(d.Hour())*10000 + int64(d.Minute())*100 + int64(d.Second())
case "HOUR_MINUTE":
return int64(d.Hour())*100 + int64(d.Minute()), nil
res = int64(d.Hour())*100 + int64(d.Minute())
case "DAY_MICROSECOND":
return int64(d.Hour()*10000+d.Minute()*100+d.Second())*1000000 + int64(d.MicroSecond()), nil
res = int64(d.Hour()*10000+d.Minute()*100+d.Second())*1000000 + int64(d.MicroSecond())
case "DAY_SECOND":
return int64(d.Hour())*10000 + int64(d.Minute())*100 + int64(d.Second()), nil
res = int64(d.Hour())*10000 + int64(d.Minute())*100 + int64(d.Second())
case "DAY_MINUTE":
return int64(d.Hour())*100 + int64(d.Minute()), nil
res = int64(d.Hour())*100 + int64(d.Minute())
case "DAY_HOUR":
return int64(d.Hour()), nil
res = int64(d.Hour())
default:
return 0, errors.Errorf("invalid unit %s", unit)
}
if d.Duration < 0 {
res = -res
}
return res, nil
}

// parseSingleTimeValue parse the format according the given unit. If we set strictCheck true, we'll check whether
Expand Down
80 changes: 56 additions & 24 deletions types/time_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1556,35 +1556,67 @@ func (s *testTimeSuite) TestExtractDatetimeNum(c *C) {
}

func (s *testTimeSuite) TestExtractDurationNum(c *C) {
in := types.Duration{Duration: time.Duration(3600 * 24 * 365), Fsp: types.DefaultFsp}
tbl := []struct {
type resultTbl struct {
unit string
expect int64
}{
{"MICROSECOND", 31536},
{"SECOND", 0},
{"MINUTE", 0},
{"HOUR", 0},
{"SECOND_MICROSECOND", 31536},
{"MINUTE_MICROSECOND", 31536},
{"MINUTE_SECOND", 0},
{"HOUR_MICROSECOND", 31536},
{"HOUR_SECOND", 0},
{"HOUR_MINUTE", 0},
{"DAY_MICROSECOND", 31536},
{"DAY_SECOND", 0},
{"DAY_MINUTE", 0},
{"DAY_HOUR", 0},
}
type testCase struct {
in types.Duration
resTbls []resultTbl
}
cases := []testCase{
{
in: types.Duration{Duration: time.Duration(3600 * 24 * 365), Fsp: types.DefaultFsp},
resTbls: []resultTbl{
{"MICROSECOND", 31536},
{"SECOND", 0},
{"MINUTE", 0},
{"HOUR", 0},
{"SECOND_MICROSECOND", 31536},
{"MINUTE_MICROSECOND", 31536},
{"MINUTE_SECOND", 0},
{"HOUR_MICROSECOND", 31536},
{"HOUR_SECOND", 0},
{"HOUR_MINUTE", 0},
{"DAY_MICROSECOND", 31536},
{"DAY_SECOND", 0},
{"DAY_MINUTE", 0},
{"DAY_HOUR", 0},
},
},
{
// "-10:59:1" = -10^9 * (10 * 3600 + 59 * 60 + 1)
in: types.Duration{Duration: time.Duration(-39541000000000), Fsp: types.DefaultFsp},
resTbls: []resultTbl{
{"MICROSECOND", 0},
{"SECOND", -1},
{"MINUTE", -59},
{"HOUR", -10},
{"SECOND_MICROSECOND", -1000000},
{"MINUTE_MICROSECOND", -5901000000},
{"MINUTE_SECOND", -5901},
{"HOUR_MICROSECOND", -105901000000},
{"HOUR_SECOND", -105901},
{"HOUR_MINUTE", -1059},
{"DAY_MICROSECOND", -105901000000},
{"DAY_SECOND", -105901},
{"DAY_MINUTE", -1059},
{"DAY_HOUR", -10},
},
},
}

for _, col := range tbl {
res, err := types.ExtractDurationNum(&in, col.unit)
c.Assert(err, IsNil)
c.Assert(res, Equals, col.expect)
for _, testcase := range cases {
in := testcase.in
for _, col := range testcase.resTbls {
res, err := types.ExtractDurationNum(&in, col.unit)
c.Assert(err, IsNil)
c.Assert(res, Equals, col.expect)
}
res, err := types.ExtractDurationNum(&in, "TEST_ERROR")
c.Assert(res, Equals, int64(0))
c.Assert(err, ErrorMatches, "invalid unit.*")
}
res, err := types.ExtractDurationNum(&in, "TEST_ERROR")
c.Assert(res, Equals, int64(0))
c.Assert(err, ErrorMatches, "invalid unit.*")
}

func (s *testTimeSuite) TestParseDurationValue(c *C) {
Expand Down

0 comments on commit f529696

Please sign in to comment.