diff --git a/enginetest/queries/queries.go b/enginetest/queries/queries.go index 79d1192e2e..421b28263f 100644 --- a/enginetest/queries/queries.go +++ b/enginetest/queries/queries.go @@ -799,7 +799,7 @@ var QueryTests = []QueryTest{ { // Assert that SYSDATE() returns different times on each call in a query (unlike NOW()) // Using the maximum precision for fractional seconds, lets us see a difference. - Query: "select now() = sysdate(), sleep(0.1), now(6) < sysdate(6);", + Query: "select now() = sysdate(), sleep(0.5), now(6) < sysdate(6);", Expected: []sql.Row{{true, 0, true}}, }, { diff --git a/enginetest/queries/script_queries.go b/enginetest/queries/script_queries.go index 991b04df7b..1956b88f35 100644 --- a/enginetest/queries/script_queries.go +++ b/enginetest/queries/script_queries.go @@ -5128,6 +5128,34 @@ CREATE TABLE tab3 ( }, }, }, + { + Dialect: "mysql", + Name: "UNIX_TIMESTAMP function preserves trailing 0s", + SetUpScript: []string{ + "SET time_zone = '+07:00';", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "select unix_timestamp('2001-02-03 12:34:56.10');", + Expected: []sql.Row{ + {"981178496.10"}, + }, + }, + { + Query: "select unix_timestamp('2001-02-03 12:34:56.000000');", + Expected: []sql.Row{ + {"981178496.000000"}, + }, + }, + { + Query: "select unix_timestamp('2001-02-03 12:34:56.1234567');", + Expected: []sql.Row{ + {"981178496.123457"}, + }, + }, + }, + }, + { Name: "Querying existing view that references non-existing table", SetUpScript: []string{ diff --git a/sql/expression/function/date.go b/sql/expression/function/date.go index 1d12893b05..e2568b54f2 100644 --- a/sql/expression/function/date.go +++ b/sql/expression/function/date.go @@ -476,6 +476,26 @@ func NewUnixTimestamp(args ...sql.Expression) (sql.Expression, error) { if err != nil || date == nil { return &UnixTimestamp{Date: arg}, nil } + // special case: text types with fractional seconds preserve scale + // e.g. '2000-01-02 12:34:56.000' -> scale 3 + if types.IsText(arg.Type()) { + dateStr := date.(string) + idx := strings.Index(dateStr, ".") + if idx != -1 { + dateStr = strings.TrimSpace(dateStr[idx:]) + scale := uint8(len(dateStr) - 1) + if scale > 0 { + if scale > 6 { + scale = 6 + } + typ, tErr := types.CreateDecimalType(19, scale) + if tErr != nil { + return nil, tErr + } + return &UnixTimestamp{Date: arg, typ: typ}, nil + } + } + } date, _, err = types.DatetimeMaxPrecision.Convert(date) if err != nil { return &UnixTimestamp{Date: arg}, nil diff --git a/sql/expression/function/time_test.go b/sql/expression/function/time_test.go index e96241c204..985907b1d0 100644 --- a/sql/expression/function/time_test.go +++ b/sql/expression/function/time_test.go @@ -16,6 +16,7 @@ package function import ( "fmt" + "math" "testing" "time" @@ -377,7 +378,7 @@ func TestTime_Microsecond(t *testing.T) { {"null date", sql.NewRow(nil), nil, false}, {"invalid type", sql.NewRow([]byte{0, 1, 2}), nil, true}, {"date as string", sql.NewRow(stringDate), uint64(0), false}, - {"date as time", sql.NewRow(currTime), uint64(currTime.Nanosecond()) / uint64(time.Microsecond), false}, + {"date as time", sql.NewRow(currTime), uint64(math.Round(float64(currTime.Nanosecond()) / float64(time.Microsecond))), false}, } for _, tt := range testCases { diff --git a/sql/types/datetime.go b/sql/types/datetime.go index 68f8f7db41..01813d87b8 100644 --- a/sql/types/datetime.go +++ b/sql/types/datetime.go @@ -198,10 +198,10 @@ func ConvertToTime(v interface{}, t datetimeType) (time.Time, error) { return zeroTime, nil } - // Truncate the date to the precision of this type + // Round the date to the precision of this type truncationDuration := time.Second truncationDuration /= time.Duration(precisionConversion[t.precision]) - res = res.Truncate(truncationDuration) + res = res.Round(truncationDuration) switch t.baseType { case sqltypes.Date: diff --git a/sql/types/datetime_test.go b/sql/types/datetime_test.go index 5847df728e..df61f35f49 100644 --- a/sql/types/datetime_test.go +++ b/sql/types/datetime_test.go @@ -57,7 +57,7 @@ func TestDatetimeCompare(t *testing.T) { {DatetimeMaxPrecision, "2010-06-03 06:03:11.123456111", "2010-06-03 06:03:11.123456333", 0}, {MustCreateDatetimeType(sqltypes.Datetime, 3), "2010-06-03 06:03:11.123", "2010-06-03 06:03:11", 1}, {MustCreateDatetimeType(sqltypes.Datetime, 3), "2010-06-03 06:03:11", "2010-06-03 06:03:11.123", -1}, - {MustCreateDatetimeType(sqltypes.Datetime, 3), "2010-06-03 06:03:11.123456", "2010-06-03 06:03:11.123789", 0}, + {MustCreateDatetimeType(sqltypes.Datetime, 3), "2010-06-03 06:03:11.123456", "2010-06-03 06:03:11.123789", -1}, {Timestamp, time.Date(2012, 12, 12, 12, 12, 12, 12, time.UTC), time.Date(2012, 12, 12, 12, 24, 24, 24, time.UTC), -1}, {Timestamp, time.Date(2012, 12, 12, 12, 12, 12, 12, time.UTC), @@ -219,9 +219,9 @@ func TestDatetimeConvert(t *testing.T) { time.Date(2012, 12, 12, 12, 12, 12, 0, time.UTC), false}, {Datetime, "2010-06-03 12:12:12.123456", time.Date(2010, 6, 3, 12, 12, 12, 0, time.UTC), false}, {Datetime, "2010-06-03T12:12:12.123456Z", time.Date(2010, 6, 3, 12, 12, 12, 0, time.UTC), false}, - {Datetime, "2010-06-03 12:34:56.7", time.Date(2010, 6, 3, 12, 34, 56, 0, time.UTC), false}, - {Datetime, "2010-06-03 12:34:56.78", time.Date(2010, 6, 3, 12, 34, 56, 0, time.UTC), false}, - {Datetime, "2010-06-03 12:34:56.789", time.Date(2010, 6, 3, 12, 34, 56, 0, time.UTC), false}, + {Datetime, "2010-06-03 12:34:56.7", time.Date(2010, 6, 3, 12, 34, 57, 0, time.UTC), false}, + {Datetime, "2010-06-03 12:34:56.78", time.Date(2010, 6, 3, 12, 34, 57, 0, time.UTC), false}, + {Datetime, "2010-06-03 12:34:56.789", time.Date(2010, 6, 3, 12, 34, 57, 0, time.UTC), false}, {TimestampMaxPrecision, nil, nil, false}, {TimestampMaxPrecision, time.Date(2012, 12, 12, 12, 12, 12, 12, time.UTC), @@ -258,9 +258,9 @@ func TestDatetimeConvert(t *testing.T) { time.Date(2012, 12, 12, 12, 12, 12, 0, time.UTC), false}, {Timestamp, "2010-06-03 12:12:12.123456", time.Date(2010, 6, 3, 12, 12, 12, 0, time.UTC), false}, {Timestamp, "2010-06-03T12:12:12.123456Z", time.Date(2010, 6, 3, 12, 12, 12, 0, time.UTC), false}, - {Timestamp, "2010-06-03 12:34:56.7", time.Date(2010, 6, 3, 12, 34, 56, 0, time.UTC), false}, - {Timestamp, "2010-06-03 12:34:56.78", time.Date(2010, 6, 3, 12, 34, 56, 0, time.UTC), false}, - {Timestamp, "2010-06-03 12:34:56.789", time.Date(2010, 6, 3, 12, 34, 56, 0, time.UTC), false}, + {Timestamp, "2010-06-03 12:34:56.7", time.Date(2010, 6, 3, 12, 34, 57, 0, time.UTC), false}, + {Timestamp, "2010-06-03 12:34:56.78", time.Date(2010, 6, 3, 12, 34, 57, 0, time.UTC), false}, + {Timestamp, "2010-06-03 12:34:56.789", time.Date(2010, 6, 3, 12, 34, 57, 0, time.UTC), false}, {Date, "0000-01-01 00:00:00", time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, {Date, "0500-01-01 00:00:00", time.Date(500, 1, 1, 0, 0, 0, 0, time.UTC), false},