From 636a8b1f2cef7fa63568aa380acab1bb51741d4e Mon Sep 17 00:00:00 2001 From: Zijie Lu Date: Fri, 11 Jun 2021 10:05:30 +0800 Subject: [PATCH] util: Fix interval division with float rounds differently from PostgreSQL Before this commit, the nanos will be rounded in `MakeDuration`. This commit round down nanos before `rounded` is called in the div of duration. Fixes #66118 Release note (bug fix): Fixed a bug with PostgreSQL compatibility where dividing an interval by a number would round to the nearest Microsecond instead of always rounding down. --- pkg/sql/logictest/testdata/logic_test/interval | 9 +++++++++ pkg/util/duration/duration.go | 5 ++++- pkg/util/duration/duration_test.go | 12 ++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/pkg/sql/logictest/testdata/logic_test/interval b/pkg/sql/logictest/testdata/logic_test/interval index d8fb3d0a891f..86216b85fa17 100644 --- a/pkg/sql/logictest/testdata/logic_test/interval +++ b/pkg/sql/logictest/testdata/logic_test/interval @@ -367,3 +367,12 @@ subtest regression_62369 query error "10000000000000000000000000000000000": value out of range SELECT INTERVAL '10000000000000000000000000000000000 year' + +query T +SELECT i / 2 FROM ( VALUES + ('0 days 0.253000 seconds'::interval), + (INTERVAL '0.000001'::interval) +) regression_66118(i) +---- +00:00:00.126 +00:00:00 diff --git a/pkg/util/duration/duration.go b/pkg/util/duration/duration.go index 70e8311463e4..8656cd2e766b 100644 --- a/pkg/util/duration/duration.go +++ b/pkg/util/duration/duration.go @@ -619,9 +619,12 @@ func (d Duration) MulFloat(x float64) Duration { func (d Duration) DivFloat(x float64) Duration { monthInt, monthFrac := math.Modf(float64(d.Months) / x) dayInt, dayFrac := math.Modf((float64(d.Days) / x) + (monthFrac * DaysPerMonth)) + // We ensure we round down nanoseconds when doing division to + // match PostgreSQL. + retNanos := time.Duration(float64(d.nanos)/x).Truncate(time.Millisecond) return MakeDuration( - int64((float64(d.nanos)/x)+(dayFrac*float64(nanosInDay))), + int64(float64(retNanos.Nanoseconds())+(dayFrac*float64(nanosInDay))), int64(dayInt), int64(monthInt), ) diff --git a/pkg/util/duration/duration_test.go b/pkg/util/duration/duration_test.go index 78d25b166b49..da10335f1ffd 100644 --- a/pkg/util/duration/duration_test.go +++ b/pkg/util/duration/duration_test.go @@ -498,6 +498,18 @@ func TestFloatMath(t *testing.T) { Duration{Months: 2, Days: 34, nanos: nanosInHour * 4}, Duration{Days: 23, nanos: nanosInHour * 13}, }, + { + Duration{Months: 0, Days: 0, nanos: nanosInSecond * 0.253000}, + 3.2, + Duration{Months: 0, Days: 0, nanos: nanosInSecond * 0.8096}, + Duration{Months: 0, Days: 0, nanos: nanosInSecond * 0.079062}, + }, + { + Duration{Months: 0, Days: 0, nanos: nanosInSecond * 0.000001}, + 2.0, + Duration{Months: 0, Days: 0, nanos: nanosInSecond * 0.000002}, + Duration{Months: 0, Days: 0, nanos: nanosInSecond * 0}, + }, } for i, test := range tests {