diff --git a/enginetest/queries/function_queries.go b/enginetest/queries/function_queries.go index 4d60fbe4b9..954a3c3b3b 100644 --- a/enginetest/queries/function_queries.go +++ b/enginetest/queries/function_queries.go @@ -1601,12 +1601,32 @@ var FunctionQueryTests = []QueryTest{ Query: "select abs(-i) from mytable order by 1", Expected: []sql.Row{{1}, {2}, {3}}, }, - + // https://github.com/dolthub/dolt/issues/9735 + { + Query: "select log('10asdf', '100f')", + Expected: []sql.Row{{float64(2)}}, + }, + { + Query: "select log('a10asdf', 'b100f')", + Expected: []sql.Row{{nil}}, + }, // Date Manipulation Function Tests { Query: "SELECT TIMESTAMPADD(DAY, 1, '2018-05-02')", Expected: []sql.Row{{"2018-05-03"}}, }, + { + Query: "select timestampadd(day, 1, '0000-00-00')", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select timestampadd(day, 1, 0)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, { Query: "SELECT DATE_ADD('2018-05-02', INTERVAL 1 day)", Expected: []sql.Row{{"2018-05-03"}}, @@ -1619,6 +1639,18 @@ var FunctionQueryTests = []QueryTest{ Query: "select date_add(time('12:13:14'), interval 1 minute);", Expected: []sql.Row{{types.Timespan(44054000000)}}, }, + { + Query: "select date_add(0, interval 1 day)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select date_sub(0, interval 1 day)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, { Query: "SELECT DATE_SUB('2018-05-02', INTERVAL 1 DAY)", Expected: []sql.Row{{"2018-05-01"}}, @@ -1663,6 +1695,42 @@ var FunctionQueryTests = []QueryTest{ Query: "SELECT DATE_ADD('9999-12-31 23:59:59', INTERVAL 1 DAY)", Expected: []sql.Row{{nil}}, }, + { + Query: "select 0 + interval 1 day", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select 0 - interval 1 day", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select datediff(0, '2020-10-10')", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select datediff('0000-00-00', '2020-10-10')", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select datediff('2020-10-10', 0)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select datediff('2020-10-10', '0000-00-00')", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, { Query: "SELECT EXTRACT(DAY FROM '9999-12-31 23:59:59')", Expected: []sql.Row{{31}}, @@ -1755,17 +1823,641 @@ var FunctionQueryTests = []QueryTest{ {uint32(1000)}, }, }, -} - -// BrokenFunctionQueryTests contains SQL function call queries that don't match MySQL behavior -var BrokenFunctionQueryTests = []QueryTest{ - // https://github.com/dolthub/dolt/issues/9735 + // date-related functions { - Query: "select log('10asdf', '100f')", - Expected: []sql.Row{{float64(2)}}, + Query: "select day(0)", + Expected: []sql.Row{{0}}, }, { - Query: "select log('a10asdf', 'b100f')", - Expected: []sql.Row{{nil}}, + Query: "select day(false)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select day(true)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select day('0000-00-00')", + Expected: []sql.Row{{0}}, + }, + { + Query: "select day('0000-01-01')", + Expected: []sql.Row{{1}}, + }, + { + Query: "select dayname(0)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select dayname(false)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select dayname(true)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select dayname('0000-00-00')", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select dayname('0000-01-01')", + // This is Sunday in MySQL. It seems like Go's time library considers 0000-02-29 a valid date but MySQL does + // not. This is why the days of the week are off. 0000 is not a real year anyway. This test is to make sure + // 0000-01-01 is not interpreted as zero time + Expected: []sql.Row{{"Saturday"}}, + }, + { + Query: "select dayname('2025-11-13')", + Expected: []sql.Row{{"Thursday"}}, + }, + { + Query: "select dayofmonth(0)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select dayofmonth(false)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select dayofmonth(true)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select dayofmonth('0000-00-00')", + Expected: []sql.Row{{0}}, + }, + { + Query: "select dayofmonth('0000-01-01')", + Expected: []sql.Row{{1}}, + }, + { + Query: "select dayofweek(0)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select dayofweek(false)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select dayofweek(true)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select dayofweek('0000-00-00')", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select dayofweek('0000-01-01')", + // This is 1 (Sunday) in MySQL. It seems like Go's time library considers 0000-02-29 a valid date but MySQL does + // not. This is why the days of the week are off. 0000 is not a real year anyway. This test is to make sure + // 0000-01-01 is not interpreted as zero time + Expected: []sql.Row{{7}}, + }, + { + Query: "select dayofweek('2025-11-13')", + Expected: []sql.Row{{5}}, + }, + { + Query: "select dayofyear(0)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select dayofyear(false)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select dayofyear(true)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select dayofyear('0000-00-00')", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select dayofyear('0000-01-01')", + Expected: []sql.Row{{1}}, + }, + { + Query: "select month(0)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select month(false)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select month(true)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select month('0000-00-00')", + Expected: []sql.Row{{0}}, + }, + { + Query: "select month('0000-01-01')", + Expected: []sql.Row{{1}}, + }, + { + Query: "select monthname(0)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select monthname(false)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select monthname(true)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select monthname('0000-00-00')", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select monthname('0000-01-01')", + Expected: []sql.Row{{"January"}}, + }, + { + Query: "select week(0)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select week(false)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select week(true)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select week('0000-00-00')", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select week('0000-01-01')", + Expected: []sql.Row{{1}}, + }, + { + Query: "select weekday(0)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select weekday(false)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select weekday(true)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select weekday('0000-00-00')", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select weekday('0000-01-01')", + // This is 6 (Sunday) in MySQL. It seems like Go's time library considers 0000-02-29 a valid date but MySQL does + // not. This is why the days of the week are off. 0000 is not a real year anyway. This test is to make sure + // 0000-01-01 is not interpreted as zero time + Expected: []sql.Row{{5}}, + }, + { + Query: "select weekday('2025-11-13')", + Expected: []sql.Row{{3}}, + }, + { + Query: "select weekofyear(0)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select weekofyear(false)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select weekofyear(true)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select weekofyear('0000-00-00')", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select weekofyear('0000-01-01')", + Expected: []sql.Row{{52}}, + }, + { + Query: "select yearweek(0)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select yearweek(false)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select yearweek(true)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select yearweek('0000-00-00')", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select yearweek('0000-01-01')", + Expected: []sql.Row{{1}}, + }, + { + Query: "select quarter(0)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select quarter(false)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select quarter(true)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select quarter('0000-00-00')", + Expected: []sql.Row{{0}}, + }, + { + Query: "select quarter('0000-01-01')", + Expected: []sql.Row{{1}}, + }, + { + Query: "select date('0000-01-01')", + Expected: []sql.Row{{"0000-01-01"}}, + }, + { + Query: "select date('0000-00-00')", + Expected: []sql.Row{{"0000-00-00"}}, + }, + { + Query: "select date(0)", + Expected: []sql.Row{{"0000-00-00"}}, + }, + { + Query: "select date(false)", + Expected: []sql.Row{{"0000-00-00"}}, + }, + { + Query: "select date(true)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select extract(day from 0)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(day from false)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(day from true)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select extract(week from 0)", + // This is 613566757 in MySQL but that value seems related to this bug https://bugs.mysql.com/bug.php?id=71414&files=1 + Expected: []sql.Row{{1}}, + }, + { + Query: "select extract(week from false)", + // This is 613566757 in MySQL but that value seems related to this bug https://bugs.mysql.com/bug.php?id=71414&files=1 + Expected: []sql.Row{{1}}, + }, + { + Query: "select extract(week from true)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select extract(month from 0)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(month from false)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(month from true)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select extract(quarter from 0)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(quarter from false)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(quarter from true)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select extract(year from 0)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(year from false)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(year from true)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select extract(year_month from 0)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(year_month from false)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(year_month from true)", + Expected: []sql.Row{{nil}}, + ExpectedWarning: mysql.ERTruncatedWrongValue, + ExpectedWarningsCount: 1, + }, + { + Query: "select extract(day_microsecond from 0)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(day_microsecond from false)", + Expected: []sql.Row{{0}}, + }, + { + Skip: true, + Query: "select extract(day_microsecond from true)", + Expected: []sql.Row{{1000000}}, + }, + { + Query: "select extract(day_second from 0)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(day_second from false)", + Expected: []sql.Row{{0}}, + }, + { + // https://github.com/dolthub/dolt/issues/10087 + Skip: true, + Query: "select extract(day_second from true)", + Expected: []sql.Row{{1}}, + }, + { + Query: "select extract(day_minute from 0)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(day_minute from false)", + Expected: []sql.Row{{0}}, + }, + { + // https://github.com/dolthub/dolt/issues/10087 + Skip: true, + Query: "select extract(day_minute from true)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(day_hour from 0)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(day_hour from false)", + Expected: []sql.Row{{0}}, + }, + { + // https://github.com/dolthub/dolt/issues/10087 + Skip: true, + Query: "select extract(day_hour from true)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(second_microsecond from 0)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(second_microsecond from false)", + Expected: []sql.Row{{0}}, + }, + { + // https://github.com/dolthub/dolt/issues/10087 + Skip: true, + Query: "select extract(second_microsecond from true)", + Expected: []sql.Row{{1000000}}, + }, + { + Query: "select extract(minute_microsecond from 0)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(minute_microsecond from false)", + Expected: []sql.Row{{0}}, + }, + { + // https://github.com/dolthub/dolt/issues/10087 + Skip: true, + Query: "select extract(minute_microsecond from true)", + Expected: []sql.Row{{1000000}}, + }, + { + Query: "select extract(minute_second from 0)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(minute_second from false)", + Expected: []sql.Row{{0}}, + }, + { + // https://github.com/dolthub/dolt/issues/10087 + Skip: true, + Query: "select extract(minute_second from true)", + Expected: []sql.Row{{1}}, + }, + { + Query: "select extract(hour_microsecond from 0)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(hour_microsecond from false)", + Expected: []sql.Row{{0}}, + }, + { + // https://github.com/dolthub/dolt/issues/10087 + Skip: true, + Query: "select extract(hour_microsecond from true)", + Expected: []sql.Row{{1000000}}, + }, + { + Query: "select extract(hour_second from 0)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(hour_second from false)", + Expected: []sql.Row{{0}}, + }, + { + // https://github.com/dolthub/dolt/issues/10087 + Skip: true, + Query: "select extract(hour_second from true)", + Expected: []sql.Row{{1}}, + }, + { + Query: "select extract(hour_minute from 0)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(hour_minute from false)", + Expected: []sql.Row{{0}}, + }, + { + // https://github.com/dolthub/dolt/issues/10087 + Skip: true, + Query: "select extract(hour_minute from true)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(microsecond from 0)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(microsecond from false)", + Expected: []sql.Row{{0}}, + }, + { + // https://github.com/dolthub/dolt/issues/10087 + Skip: true, + Query: "select extract(microsecond from true)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(second from 0)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(second from false)", + Expected: []sql.Row{{0}}, + }, + { + // https://github.com/dolthub/dolt/issues/10087 + Skip: true, + Query: "select extract(second from true)", + Expected: []sql.Row{{1}}, + }, + { + Query: "select extract(minute from 0)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(minute from false)", + Expected: []sql.Row{{0}}, + }, + { + // https://github.com/dolthub/dolt/issues/10087 + Skip: true, + Query: "select extract(minute from true)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(hour from 0)", + Expected: []sql.Row{{0}}, + }, + { + Query: "select extract(hour from false)", + Expected: []sql.Row{{0}}, + }, + { + // https://github.com/dolthub/dolt/issues/10087 + Skip: true, + Query: "select extract(hour from true)", + Expected: []sql.Row{{0}}, }, } diff --git a/enginetest/queries/insert_queries.go b/enginetest/queries/insert_queries.go index eff5a80b2b..493eaa5445 100644 --- a/enginetest/queries/insert_queries.go +++ b/enginetest/queries/insert_queries.go @@ -2349,6 +2349,26 @@ var InsertScripts = []ScriptTest{ }, }, }, + { + Name: "inserting zero date", + Dialect: "mysql", + SetUpScript: []string{ + "create table t(d date)", + "insert into t values ('0000-00-00')", + "create table t2(d datetime)", + "insert into t2 values ('0000-00-00')", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "select * from t", + Expected: []sql.Row{{types.ZeroTime}}, + }, + { + Query: "select * from t2", + Expected: []sql.Row{{types.ZeroTime}}, + }, + }, + }, } var InsertDuplicateKeyKeyless = []ScriptTest{ diff --git a/enginetest/queries/queries.go b/enginetest/queries/queries.go index 36cf975d66..fb0619fc41 100644 --- a/enginetest/queries/queries.go +++ b/enginetest/queries/queries.go @@ -8352,13 +8352,6 @@ from typestable`, }, }, }, - { - // TODO: This goes past MySQL's range - Query: "select dayname('0000-00-00')", - Expected: []sql.Row{ - {"Saturday"}, - }, - }, { Query: "select * from mytable order by dayname(i)", Expected: []sql.Row{ diff --git a/enginetest/queries/script_queries.go b/enginetest/queries/script_queries.go index 387449357d..5de345fa06 100644 --- a/enginetest/queries/script_queries.go +++ b/enginetest/queries/script_queries.go @@ -6175,10 +6175,10 @@ CREATE TABLE tab3 ( "0", float64(0), float64(0), - time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), + time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), types.Timespan(0), - time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), - time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), + time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), + time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), 0, "", "", diff --git a/enginetest/queries/update_queries.go b/enginetest/queries/update_queries.go index 86663d6cb5..34dae84137 100644 --- a/enginetest/queries/update_queries.go +++ b/enginetest/queries/update_queries.go @@ -1011,7 +1011,7 @@ var UpdateErrorScripts = []ScriptTest{ }, } -var ZeroTime = time.Date(0000, time.January, 1, 0, 0, 0, 0, time.UTC) +var ZeroTime = time.Date(0000, 0, 0, 0, 0, 0, 0, time.UTC) var Jan1Noon = time.Date(2000, time.January, 1, 12, 0, 0, 0, time.UTC) var Dec15_1_30 = time.Date(2023, time.December, 15, 1, 30, 0, 0, time.UTC) var Oct2Midnight = time.Date(2020, time.October, 2, 0, 0, 0, 0, time.UTC) diff --git a/sql/expression/arithmetic.go b/sql/expression/arithmetic.go index f4a4c101ae..dc42d6a51d 100644 --- a/sql/expression/arithmetic.go +++ b/sql/expression/arithmetic.go @@ -440,6 +440,13 @@ func convertValueToType(ctx *sql.Context, typ sql.Type, val interface{}, isTimeT // the value is interpreted as 0, but we need to match the type of the other valid value // to avoid additional conversion, the nil value is handled in each operation } + if types.IsTime(typ) { + time, ok := cval.(time.Time) + if !ok || time.Equal(types.ZeroTime) { + ctx.Warn(1292, "Incorrect datetime value: '%s'", val) + return nil + } + } return cval } @@ -462,6 +469,9 @@ func convertTimeTypeToString(val interface{}) interface{} { } func plus(lval, rval interface{}) (interface{}, error) { + if lval == nil || rval == nil { + return nil, nil + } switch l := lval.(type) { case uint8: switch r := rval.(type) { @@ -536,6 +546,9 @@ func plus(lval, rval interface{}) (interface{}, error) { } func minus(lval, rval interface{}) (interface{}, error) { + if lval == nil || rval == nil { + return nil, nil + } switch l := lval.(type) { case uint8: switch r := rval.(type) { diff --git a/sql/expression/function/days.go b/sql/expression/function/days.go index 0fd34033a8..5208b42dd7 100644 --- a/sql/expression/function/days.go +++ b/sql/expression/function/days.go @@ -98,6 +98,9 @@ func (t *ToDays) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { return nil, nil } d := date.(time.Time) + if d.Equal(types.ZeroTime) { + return nil, nil + } // Using zeroTime.Sub(date) doesn't work because it overflows time.Duration // so we need to calculate the number of days manually diff --git a/sql/expression/function/days_test.go b/sql/expression/function/days_test.go index eb162b2832..9179333465 100644 --- a/sql/expression/function/days_test.go +++ b/sql/expression/function/days_test.go @@ -45,7 +45,10 @@ func TestToDays(t *testing.T) { arg: expression.NewLiteral("-10", types.Int32), exp: nil, }, - + { + arg: expression.NewLiteral("0", types.Int32), + exp: nil, + }, { arg: expression.NewLiteral("0000-00-00", types.Text), exp: nil, diff --git a/sql/expression/function/extract.go b/sql/expression/function/extract.go index 3d2fbcf689..7332a03570 100644 --- a/sql/expression/function/extract.go +++ b/sql/expression/function/extract.go @@ -114,7 +114,7 @@ func (td *Extract) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { switch unit { case "DAY": - return dateTime.Day(), nil + return day(dateTime), nil case "HOUR": return dateTime.Hour(), nil case "MINUTE": @@ -124,57 +124,66 @@ func (td *Extract) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { case "MICROSECOND": return dateTime.Nanosecond() / 1000, nil case "QUARTER": - return (int(dateTime.Month())-1)/3 + 1, nil + return quarter(dateTime), nil case "MONTH": - return int(dateTime.Month()), nil + return month(dateTime), nil case "WEEK": - date, err := getDate(ctx, expression.UnaryExpression{Child: td.RightChild}, row) - if err != nil { - return nil, err - } - yyyy, ok := year(date).(int32) + yyyy, ok := year(dateTime).(int) if !ok { return nil, sql.ErrInvalidArgumentDetails.New("WEEK", "invalid year") } - mm, ok := month(date).(int32) + mm, ok := month(dateTime).(int) if !ok { return nil, sql.ErrInvalidArgumentDetails.New("WEEK", "invalid month") } - dd, ok := day(date).(int32) + dd, ok := day(dateTime).(int) if !ok { return nil, sql.ErrInvalidArgumentDetails.New("WEEK", "invalid day") } - yearForWeek, week := calcWeek(yyyy, mm, dd, weekBehaviourYear) - if yearForWeek < yyyy { + yr := int32(yyyy) + yearForWeek, week := calcWeek(yr, int32(mm), int32(dd), weekBehaviourYear) + if yearForWeek < yr { week = 0 - } else if yearForWeek > yyyy { + } else if yearForWeek > yr { week = 53 } return int(week), nil case "YEAR": - return dateTime.Year(), nil + return year(dateTime), nil case "DAY_HOUR": - dd := dateTime.Day() * 1_00 + dd, ok := day(dateTime).(int) + if !ok { + return nil, sql.ErrInvalidArgumentDetails.New("DAY_HOUR", "invalid day") + } hh := dateTime.Hour() - return dd + hh, nil + return (dd * 1_00) + hh, nil case "DAY_MINUTE": - dd := dateTime.Day() * 1_00_00 + dd, ok := day(dateTime).(int) + if !ok { + return nil, sql.ErrInvalidArgumentDetails.New("DAY_MINUTE", "invalid day") + } hh := dateTime.Hour() * 1_00 mm := dateTime.Minute() - return dd + hh + mm, nil + return (dd * 1_00_00) + hh + mm, nil case "DAY_SECOND": - dd := dateTime.Day() * 1_00_00_00 + dd, ok := day(dateTime).(int) + if !ok { + return nil, sql.ErrInvalidArgumentDetails.New("DAY_SECOND", "invalid day") + } hh := dateTime.Hour() * 1_00_00 mm := dateTime.Minute() * 1_00 ss := dateTime.Second() - return dd + hh + mm + ss, nil + return (dd * 1_00_00_00) + hh + mm + ss, nil case "DAY_MICROSECOND": - dd := dateTime.Day() * 1_00_00_00_000000 + dd, ok := day(dateTime).(int) + if !ok { + return nil, sql.ErrInvalidArgumentDetails.New("DAY_MICROSECOND", "invalid day") + } hh := dateTime.Hour() * 1_00_00_000000 mm := dateTime.Minute() * 1_00_000000 ss := dateTime.Second() * 1_000000 mmmmmm := dateTime.Nanosecond() / 1000 - return dd + hh + mm + ss + mmmmmm, nil + return (dd * 1_00_00_00_000000) + hh + mm + ss + mmmmmm, nil case "HOUR_MINUTE": hh := dateTime.Hour() * 1_00 mm := dateTime.Minute() @@ -204,10 +213,15 @@ func (td *Extract) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { mmmmmm := dateTime.Nanosecond() / 1000 return ss + mmmmmm, nil case "YEAR_MONTH": - yyyy := dateTime.Year() * 1_00 - dateTime.Month() - mm := int(dateTime.Month()) - return yyyy + mm, nil + yyyy, ok := year(dateTime).(int) + if !ok { + return nil, sql.ErrInvalidArgumentDetails.New("YEAR_MONTH", "invalid year") + } + mm, ok := month(dateTime).(int) + if !ok { + return nil, sql.ErrInvalidArgumentDetails.New("YEAR_MONTH", "invalid month") + } + return (yyyy * 1_00) + mm, nil default: return nil, fmt.Errorf("invalid time unit") } diff --git a/sql/expression/function/registry.go b/sql/expression/function/registry.go index 8a6fccbc66..6b00c9b48e 100644 --- a/sql/expression/function/registry.go +++ b/sql/expression/function/registry.go @@ -320,7 +320,6 @@ var BuiltIns = []sql.Function{ sql.Function0{Name: "uuid", Fn: NewUUIDFunc}, sql.Function0{Name: "uuid_short", Fn: NewUUIDShortFunc}, sql.FunctionN{Name: "uuid_to_bin", Fn: NewUUIDToBin}, - sql.FunctionN{Name: "week", Fn: NewWeek}, sql.Function1{Name: "values", Fn: NewValues}, sql.Function1{Name: "validate_password_strength", Fn: NewValidatePasswordStrength}, sql.Function1{Name: "variance", Fn: func(e sql.Expression) sql.Expression { return aggregation.NewVarPop(e) }}, @@ -338,6 +337,7 @@ var BuiltIns = []sql.Function{ sql.Function1{Name: "vector_to_string", Fn: vector.NewVectorToString}, sql.Function1{Name: "from_vector", Fn: vector.NewVectorToString}, sql.Function1{Name: "vec_totext", Fn: vector.NewVectorToString}, + sql.FunctionN{Name: "week", Fn: NewWeek}, sql.Function1{Name: "weekday", Fn: NewWeekday}, sql.Function1{Name: "weekofyear", Fn: NewWeekOfYear}, sql.Function1{Name: "year", Fn: NewYear}, diff --git a/sql/expression/function/time.go b/sql/expression/function/time.go index decbb17340..76d3026fe0 100644 --- a/sql/expression/function/time.go +++ b/sql/expression/function/time.go @@ -35,15 +35,7 @@ var ErrUnknownType = errors.NewKind("function '%s' encountered unknown type %T") var ErrTooHighPrecision = errors.NewKind("Too-big precision %d for '%s'. Maximum is %d.") -func getDate(ctx *sql.Context, - u expression.UnaryExpression, - row sql.Row) (interface{}, error) { - - val, err := u.Child.Eval(ctx, row) - if err != nil { - return nil, err - } - +func getDate(ctx *sql.Context, val interface{}) (interface{}, error) { if val == nil { return nil, nil } @@ -52,7 +44,6 @@ func getDate(ctx *sql.Context, if err != nil { ctx.Warn(1292, "Incorrect datetime value: '%s'", val) return nil, nil - //date = types.DatetimeMaxPrecision.Zero().(time.Time) } return date, nil @@ -62,13 +53,24 @@ func getDatePart(ctx *sql.Context, u expression.UnaryExpression, row sql.Row, f func(interface{}) interface{}) (interface{}, error) { + val, err := u.Child.Eval(ctx, row) + if err != nil { + return nil, err + } - date, err := getDate(ctx, u, row) + date, err := getDate(ctx, val) if err != nil { return nil, err } + if date == nil { + return nil, nil + } - return f(date), nil + part := f(date) + if part == nil { + ctx.Warn(1292, "Incorrect datetime value: '%s'", val) + } + return part, nil } // Year is a function that returns the year of a date. @@ -151,16 +153,7 @@ func (q *Quarter) CollationCoercibility(ctx *sql.Context) (collation sql.Collati // Eval implements the Expression interface. func (q *Quarter) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { - mon, err := getDatePart(ctx, q.UnaryExpression, row, month) - if err != nil { - return nil, err - } - - if mon == nil { - return nil, nil - } - - return (mon.(int32)-1)/3 + 1, nil + return getDatePart(ctx, q.UnaryExpression, row, quarter) } // WithChildren implements the Expression interface. @@ -541,13 +534,13 @@ func (d *DayOfYear) WithChildren(children ...sql.Expression) (sql.Expression, er return NewDayOfYear(children[0]), nil } -func datePartFunc(fn func(time.Time) int) func(interface{}) interface{} { +func datePartFunc(fn func(time.Time) interface{}) func(interface{}) interface{} { return func(v interface{}) interface{} { if v == nil { return nil } - return int32(fn(v.(time.Time))) + return fn(v.(time.Time)) } } @@ -602,22 +595,33 @@ func (*YearWeek) CollationCoercibility(ctx *sql.Context) (collation sql.Collatio // Eval implements the Expression interface. func (d *YearWeek) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { - date, err := getDate(ctx, expression.UnaryExpression{Child: d.date}, row) + dateVal, err := d.date.Eval(ctx, row) + if err != nil { + return nil, err + } + date, err := getDate(ctx, dateVal) if err != nil { return nil, err } if date == nil { return nil, nil } - yyyy, ok := year(date).(int32) + + dateTime, ok := date.(time.Time) + if !ok || dateTime.Equal(types.ZeroTime) { + ctx.Warn(1292, "%s", types.ErrConvertingToTime.New(dateVal).Error()) + return nil, nil + } + + yyyy, ok := year(date).(int) if !ok { return nil, sql.ErrInvalidArgumentDetails.New("YEARWEEK", "invalid year") } - mm, ok := month(date).(int32) + mm, ok := month(date).(int) if !ok { return nil, sql.ErrInvalidArgumentDetails.New("YEARWEEK", "invalid month") } - dd, ok := day(date).(int32) + dd, ok := day(date).(int) if !ok { return nil, sql.ErrInvalidArgumentDetails.New("YEARWEEK", "invalid day") } @@ -634,9 +638,9 @@ func (d *YearWeek) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { } } } - yyyy, week := calcWeek(yyyy, mm, dd, weekMode(mode)|weekBehaviourYear) + yr, week := calcWeek(int32(yyyy), int32(mm), int32(dd), weekMode(mode)|weekBehaviourYear) - return (yyyy * 100) + week, nil + return (yr * 100) + week, nil } // Resolved implements the Expression interface. @@ -710,20 +714,34 @@ func (*Week) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, // Eval implements the Expression interface. func (d *Week) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { - date, err := getDate(ctx, expression.UnaryExpression{Child: d.date}, row) + dateVal, err := d.date.Eval(ctx, row) if err != nil { return nil, err } - yyyy, ok := year(date).(int32) + date, err := getDate(ctx, dateVal) + if err != nil { + return nil, err + } + if date == nil { + return nil, nil + } + + dateTime, ok := date.(time.Time) + if !ok || dateTime.Equal(types.ZeroTime) { + ctx.Warn(1292, "%s", types.ErrConvertingToTime.New(dateVal).Error()) + return nil, nil + } + + yyyy, ok := year(date).(int) if !ok { return nil, sql.ErrInvalidArgumentDetails.New("WEEK", "invalid year") } - mm, ok := month(date).(int32) + mm, ok := month(date).(int) if !ok { return nil, sql.ErrInvalidArgumentDetails.New("WEEK", "invalid month") } - dd, ok := day(date).(int32) + dd, ok := day(date).(int) if !ok { return nil, sql.ErrInvalidArgumentDetails.New("WEEK", "invalid day") } @@ -741,11 +759,12 @@ func (d *Week) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { } } - yearForWeek, week := calcWeek(yyyy, mm, dd, weekMode(mode)|weekBehaviourYear) + yr := int32(yyyy) + yearForWeek, week := calcWeek(yr, int32(mm), int32(dd), weekMode(mode)|weekBehaviourYear) - if yearForWeek < yyyy { + if yearForWeek < yr { week = 0 - } else if yearForWeek > yyyy { + } else if yearForWeek > yr { week = 53 } @@ -871,15 +890,54 @@ func calcDaynr(yyyy, mm, dd int32) int32 { } var ( - year = datePartFunc((time.Time).Year) - month = datePartFunc(func(t time.Time) int { return int(t.Month()) }) - day = datePartFunc((time.Time).Day) - weekday = datePartFunc(func(t time.Time) int { return (int(t.Weekday()) + 6) % 7 }) - hour = datePartFunc((time.Time).Hour) - minute = datePartFunc((time.Time).Minute) - second = datePartFunc((time.Time).Second) - dayOfWeek = datePartFunc(func(t time.Time) int { return int(t.Weekday()) + 1 }) - dayOfYear = datePartFunc((time.Time).YearDay) + year = datePartFunc(func(t time.Time) interface{} { + if t.Equal(types.ZeroTime) { + return 0 + } + return t.Year() + }) + month = datePartFunc(func(t time.Time) interface{} { + if t.Equal(types.ZeroTime) { + return 0 + } + return int(t.Month()) + }) + day = datePartFunc(func(t time.Time) interface{} { + if t.Equal(types.ZeroTime) { + return 0 + } + return t.Day() + }) + weekday = datePartFunc(func(t time.Time) interface{} { + if t.Equal(types.ZeroTime) { + return nil + } + return (int(t.Weekday()) + 6) % 7 + }) + hour = datePartFunc(func(t time.Time) interface{} { return t.Hour() }) + minute = datePartFunc(func(t time.Time) interface{} { return t.Minute() }) + second = datePartFunc(func(t time.Time) interface{} { return t.Second() }) + dayOfWeek = datePartFunc(func(t time.Time) interface{} { + if t.Equal(types.ZeroTime) { + return nil + } + return int(t.Weekday()) + 1 + }) + dayOfYear = datePartFunc(func(t time.Time) interface{} { + if t.Equal(types.ZeroTime) { + return nil + } + return t.YearDay() + }) + quarter = datePartFunc(func(t time.Time) interface{} { + if t.Equal(types.ZeroTime) { + return 0 + } + return (int(t.Month())-1)/3 + 1 + }) + microsecond = datePartFunc(func(t time.Time) interface{} { + return uint64(t.Nanosecond()) / uint64(time.Microsecond) + }) ) const maxCurrTimestampPrecision = 6 @@ -1237,13 +1295,29 @@ func (*Date) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, // Eval implements the Expression interface. func (d *Date) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { - return getDatePart(ctx, d.UnaryExpression, row, func(v interface{}) interface{} { - if v == nil { - return nil - } + dateVal, err := d.Child.Eval(ctx, row) + if err != nil { + return nil, err + } - return v.(time.Time).Format("2006-01-02") - }) + date, err := getDate(ctx, dateVal) + if err != nil { + return nil, err + } + if date == nil { + return nil, nil + } + + dateTime, ok := date.(time.Time) + if !ok { + ctx.Warn(1292, "%s", types.ErrConvertingToTime.New(dateVal).Error()) + return nil, nil + } + if dateTime.Equal(types.ZeroTime) { + return types.ZeroDateStr, nil + } + + return dateTime.Format("2006-01-02"), nil } // WithChildren implements the Expression interface. @@ -1284,7 +1358,11 @@ func (dtf *UnaryDatetimeFunc) EvalChild(ctx *sql.Context, row sql.Row) (interfac } ret, _, err := types.DatetimeMaxPrecision.Convert(ctx, val) - return ret, err + if err != nil { + ctx.Warn(1292, "%s", types.ErrConvertingToTime.New(val).Error()) + return nil, nil + } + return ret, nil } // String implements the fmt.Stringer interface. @@ -1339,7 +1417,7 @@ func (d *DayName) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { } t, ok := val.(time.Time) - if !ok { + if !ok || t.Equal(types.ZeroTime) { ctx.Warn(1292, "%s", types.ErrConvertingToTime.New(val).Error()) return nil, nil } @@ -1377,20 +1455,7 @@ func NewMicrosecond(arg sql.Expression) sql.Expression { } func (m *Microsecond) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { - val, err := m.EvalChild(ctx, row) - if err != nil { - return nil, err - } - - switch v := val.(type) { - case time.Time: - return uint64(v.Nanosecond()) / uint64(time.Microsecond), nil - case nil: - return nil, nil - default: - ctx.Warn(1292, "%s", types.ErrConvertingToTime.New(val).Error()) - return nil, nil - } + return getDatePart(ctx, m.UnaryExpression, row, microsecond) } func (m *Microsecond) WithChildren(children ...sql.Expression) (sql.Expression, error) { @@ -1430,6 +1495,10 @@ func (d *MonthName) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { switch v := val.(type) { case time.Time: + if v.Equal(types.ZeroTime) { + ctx.Warn(1292, "%s", types.ErrConvertingToTime.New(val).Error()) + return nil, nil + } return v.Month().String(), nil case nil: return nil, nil @@ -1522,6 +1591,10 @@ func (m *WeekOfYear) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { switch v := val.(type) { case time.Time: + if v.Equal(types.ZeroTime) { + ctx.Warn(1292, "%s", types.ErrConvertingToTime.New(val).Error()) + return nil, nil + } _, wk := v.ISOWeek() return wk, nil case nil: diff --git a/sql/expression/function/time_math.go b/sql/expression/function/time_math.go index bd42d8c877..56db5ffd69 100644 --- a/sql/expression/function/time_math.go +++ b/sql/expression/function/time_math.go @@ -82,37 +82,47 @@ func (d *DateDiff) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { return nil, nil } - expr1, err := d.LeftChild.Eval(ctx, row) + val1, err := d.LeftChild.Eval(ctx, row) if err != nil { return nil, err } - if expr1 == nil { + if val1 == nil { return nil, nil } - expr1, _, err = types.DatetimeMaxPrecision.Convert(ctx, expr1) + expr1, _, err := types.DatetimeMaxPrecision.Convert(ctx, val1) if err != nil { - return nil, err + ctx.Warn(1292, "Incorrect datetime value: '%s'", val1) + return nil, nil } expr1str := expr1.(time.Time).String()[:10] expr1, _, _ = types.DatetimeMaxPrecision.Convert(ctx, expr1str) + if expr1 == nil { + ctx.Warn(1292, "Incorrect datetime value: '%s'", val1) + return nil, nil + } - expr2, err := d.RightChild.Eval(ctx, row) + val2, err := d.RightChild.Eval(ctx, row) if err != nil { return nil, err } - if expr2 == nil { + if val2 == nil { return nil, nil } - expr2, _, err = types.DatetimeMaxPrecision.Convert(ctx, expr2) + expr2, _, err := types.DatetimeMaxPrecision.Convert(ctx, val2) if err != nil { - return nil, err + ctx.Warn(1292, "Incorrect datetime value: '%s'", val2) + return nil, nil } expr2str := expr2.(time.Time).String()[:10] expr2, _, _ = types.DatetimeMaxPrecision.Convert(ctx, expr2str) + if expr2 == nil { + ctx.Warn(1292, "Incorrect datetime value: '%s'", val2) + return nil, nil + } date1 := expr1.(time.Time) date2 := expr2.(time.Time) @@ -237,9 +247,14 @@ func (d *DateAdd) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { ctx.Warn(1292, "%s", err.Error()) return nil, nil } + datetime, ok := dateVal.(time.Time) + if !ok || datetime.Equal(types.ZeroTime) { + ctx.Warn(1292, "Incorrect datetime value: '%s'", date) + return nil, nil + } // return appropriate type - res := types.ValidateTime(delta.Add(dateVal.(time.Time))) + res := types.ValidateTime(delta.Add(datetime)) if res == nil { return nil, nil } @@ -385,9 +400,14 @@ func (d *DateSub) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { ctx.Warn(1292, "%s", err.Error()) return nil, nil } + datetime, ok := dateVal.(time.Time) + if !ok || datetime.Equal(types.ZeroTime) { + ctx.Warn(1292, "Incorrect datetime value: '%s'", date) + return nil, nil + } // return appropriate type - res := types.ValidateTime(delta.Sub(dateVal.(time.Time))) + res := types.ValidateTime(delta.Sub(datetime)) if res == nil { return nil, nil } diff --git a/sql/expression/function/time_test.go b/sql/expression/function/time_test.go index 985907b1d0..a8ecd723ad 100644 --- a/sql/expression/function/time_test.go +++ b/sql/expression/function/time_test.go @@ -16,7 +16,6 @@ package function import ( "fmt" - "math" "testing" "time" @@ -45,8 +44,8 @@ func TestTime_Year(t *testing.T) { err bool }{ {"invalid type", sql.NewRow([]byte{0, 1, 2}), nil, false}, - {"date as string", sql.NewRow(stringDate), int32(2007), false}, - {"date as time", sql.NewRow(time.Now()), int32(time.Now().UTC().Year()), false}, + {"date as string", sql.NewRow(stringDate), 2007, false}, + {"date as time", sql.NewRow(time.Now()), time.Now().UTC().Year(), false}, } for _, tt := range testCases { @@ -75,8 +74,8 @@ func TestTime_Month(t *testing.T) { }{ {"null date", sql.NewRow(nil), nil, false}, {"invalid type", sql.NewRow([]byte{0, 1, 2}), nil, false}, - {"date as string", sql.NewRow(stringDate), int32(1), false}, - {"date as time", sql.NewRow(time.Now()), int32(time.Now().UTC().Month()), false}, + {"date as string", sql.NewRow(stringDate), 1, false}, + {"date as time", sql.NewRow(time.Now()), int(time.Now().UTC().Month()), false}, } for _, tt := range testCases { @@ -126,77 +125,77 @@ func TestTime_Quarter(t *testing.T) { { name: "date as string", row: sql.NewRow(stringDate), - expected: int32(1), + expected: 1, }, { name: "another date as string", row: sql.NewRow("2008-08-01"), - expected: int32(3), + expected: 3, }, { name: "january", row: sql.NewRow("2008-01-01"), - expected: int32(1), + expected: 1, }, { name: "february", row: sql.NewRow("2008-02-01"), - expected: int32(1), + expected: 1, }, { name: "march", row: sql.NewRow("2008-03-01"), - expected: int32(1), + expected: 1, }, { name: "april", row: sql.NewRow("2008-04-01"), - expected: int32(2), + expected: 2, }, { name: "may", row: sql.NewRow("2008-05-01"), - expected: int32(2), + expected: 2, }, { name: "june", row: sql.NewRow("2008-06-01"), - expected: int32(2), + expected: 2, }, { name: "july", row: sql.NewRow("2008-07-01"), - expected: int32(3), + expected: 3, }, { name: "august", row: sql.NewRow("2008-08-01"), - expected: int32(3), + expected: 3, }, { name: "septemeber", row: sql.NewRow("2008-09-01"), - expected: int32(3), + expected: 3, }, { name: "october", row: sql.NewRow("2008-10-01"), - expected: int32(4), + expected: 4, }, { name: "november", row: sql.NewRow("2008-11-01"), - expected: int32(4), + expected: 4, }, { name: "december", row: sql.NewRow("2008-12-01"), - expected: int32(4), + expected: 4, }, { name: "date as time", row: sql.NewRow(time.Now()), - expected: int32((time.Now().UTC().Month()-1)/3 + 1), + expected: (int(time.Now().UTC().Month())-1)/3 + 1, }, } @@ -226,8 +225,8 @@ func TestTime_Day(t *testing.T) { }{ {"null date", sql.NewRow(nil), nil, false}, {"invalid type", sql.NewRow([]byte{0, 1, 2}), nil, false}, - {"date as string", sql.NewRow(stringDate), int32(2), false}, - {"date as time", sql.NewRow(time.Now()), int32(time.Now().UTC().Day()), false}, + {"date as string", sql.NewRow(stringDate), 2, false}, + {"date as time", sql.NewRow(time.Now()), time.Now().UTC().Day(), false}, } for _, tt := range testCases { @@ -256,8 +255,8 @@ func TestTime_Weekday(t *testing.T) { }{ {"null date", sql.NewRow(nil), nil, false}, {"invalid type", sql.NewRow([]byte{0, 1, 2}), nil, false}, - {"date as string", sql.NewRow(stringDate), int32(1), false}, - {"date as time", sql.NewRow(time.Now()), int32(time.Now().UTC().Weekday()+6) % 7, false}, + {"date as string", sql.NewRow(stringDate), 1, false}, + {"date as time", sql.NewRow(time.Now()), int(time.Now().UTC().Weekday()+6) % 7, false}, } for _, tt := range testCases { @@ -286,8 +285,8 @@ func TestTime_Hour(t *testing.T) { }{ {"null date", sql.NewRow(nil), nil, false}, {"invalid type", sql.NewRow([]byte{0, 1, 2}), nil, false}, - {"date as string", sql.NewRow(stringDate), int32(14), false}, - {"date as time", sql.NewRow(time.Now()), int32(time.Now().UTC().Hour()), false}, + {"date as string", sql.NewRow(stringDate), 14, false}, + {"date as time", sql.NewRow(time.Now()), time.Now().UTC().Hour(), false}, } for _, tt := range testCases { @@ -316,8 +315,8 @@ func TestTime_Minute(t *testing.T) { }{ {"null date", sql.NewRow(nil), nil, false}, {"invalid type", sql.NewRow([]byte{0, 1, 2}), nil, false}, - {"date as string", sql.NewRow(stringDate), int32(15), false}, - {"date as time", sql.NewRow(time.Now()), int32(time.Now().UTC().Minute()), false}, + {"date as string", sql.NewRow(stringDate), 15, false}, + {"date as time", sql.NewRow(time.Now()), time.Now().UTC().Minute(), false}, } for _, tt := range testCases { @@ -346,8 +345,8 @@ func TestTime_Second(t *testing.T) { }{ {"null date", sql.NewRow(nil), nil, false}, {"invalid type", sql.NewRow([]byte{0, 1, 2}), nil, false}, - {"date as string", sql.NewRow(stringDate), int32(16), false}, - {"date as time", sql.NewRow(time.Now()), int32(time.Now().UTC().Second()), false}, + {"date as string", sql.NewRow(stringDate), 16, false}, + {"date as time", sql.NewRow(time.Now()), time.Now().UTC().Second(), false}, } for _, tt := range testCases { @@ -376,9 +375,9 @@ func TestTime_Microsecond(t *testing.T) { err bool }{ {"null date", sql.NewRow(nil), nil, false}, - {"invalid type", sql.NewRow([]byte{0, 1, 2}), nil, true}, + {"invalid type", sql.NewRow([]byte{0, 1, 2}), nil, false}, {"date as string", sql.NewRow(stringDate), uint64(0), false}, - {"date as time", sql.NewRow(currTime), uint64(math.Round(float64(currTime.Nanosecond()) / float64(time.Microsecond))), false}, + {"date as time", sql.NewRow(currTime), uint64(currTime.Nanosecond()) / uint64(time.Microsecond), false}, } for _, tt := range testCases { @@ -407,8 +406,8 @@ func TestTime_DayOfWeek(t *testing.T) { }{ {"null date", sql.NewRow(nil), nil, false}, {"invalid type", sql.NewRow([]byte{0, 1, 2}), nil, false}, - {"date as string", sql.NewRow(stringDate), int32(3), false}, - {"date as time", sql.NewRow(time.Now()), int32(time.Now().UTC().Weekday() + 1), false}, + {"date as string", sql.NewRow(stringDate), 3, false}, + {"date as time", sql.NewRow(time.Now()), int(time.Now().UTC().Weekday() + 1), false}, } for _, tt := range testCases { @@ -437,8 +436,8 @@ func TestTime_DayOfYear(t *testing.T) { }{ {"null date", sql.NewRow(nil), nil, false}, {"invalid type", sql.NewRow([]byte{0, 1, 2}), nil, false}, - {"date as string", sql.NewRow(stringDate), int32(2), false}, - {"date as time", sql.NewRow(time.Now()), int32(time.Now().UTC().YearDay()), false}, + {"date as string", sql.NewRow(stringDate), 2, false}, + {"date as time", sql.NewRow(time.Now()), time.Now().UTC().YearDay(), false}, } for _, tt := range testCases { @@ -468,7 +467,7 @@ func TestTime_WeekOfYear(t *testing.T) { err bool }{ {"null date", sql.NewRow(nil), nil, false}, - {"invalid type", sql.NewRow([]byte{0, 1, 2}), int32(1), true}, + {"invalid type", sql.NewRow([]byte{0, 1, 2}), nil, false}, {"date as string", sql.NewRow(stringDate), 1, false}, {"date as time", sql.NewRow(currTime), week, false}, } @@ -822,7 +821,7 @@ func TestTime_MonthName(t *testing.T) { err bool }{ {"null date", sql.NewRow(nil), nil, false}, - {"invalid type", sql.NewRow([]byte{0, 1, 2}), nil, true}, + {"invalid type", sql.NewRow([]byte{0, 1, 2}), nil, false}, {"time as string", sql.NewRow(stringDate), "January", false}, } diff --git a/sql/rowexec/insert_test.go b/sql/rowexec/insert_test.go index 8012440d17..d0c7c2836b 100644 --- a/sql/rowexec/insert_test.go +++ b/sql/rowexec/insert_test.go @@ -17,7 +17,6 @@ package rowexec import ( "math" "testing" - "time" "github.com/dolthub/vitess/go/sqltypes" "github.com/stretchr/testify/require" @@ -63,7 +62,7 @@ func TestInsert(t *testing.T) { colType: types.Datetime, value: "dadasd", valueType: types.Text, - expected: time.Unix(-62167219200, 0).UTC(), + expected: types.ZeroTime, warning: true, ignore: true, }, diff --git a/sql/rowexec/update_test.go b/sql/rowexec/update_test.go index 363846675a..4dbdf3e151 100644 --- a/sql/rowexec/update_test.go +++ b/sql/rowexec/update_test.go @@ -16,7 +16,6 @@ package rowexec import ( "testing" - "time" "github.com/dolthub/vitess/go/sqltypes" "github.com/stretchr/testify/require" @@ -55,7 +54,7 @@ func TestUpdateIgnoreConversions(t *testing.T) { colType: types.Datetime, value: "dadasd", valueType: types.Text, - expected: time.Unix(-62167219200, 0).UTC(), + expected: types.ZeroTime, }, { name: "inserting a negative into an unsigned int results in 0", diff --git a/sql/types/datetime.go b/sql/types/datetime.go index c10dc759fb..aca5ac146c 100644 --- a/sql/types/datetime.go +++ b/sql/types/datetime.go @@ -98,8 +98,9 @@ var ( "20060102150405", }, DateOnlyLayouts...) - // zeroTime is 0000-01-01 00:00:00 UTC which is the closest Go can get to 0000-00-00 00:00:00 - zeroTime = time.Unix(-62167219200, 0).UTC() + // zeroTime is -0001-11-30 00:00:00 UTC which is the closest Go can get to 0000-00-00 00:00:00 without conflicting + // with a valid timestamp in MySQL + ZeroTime = time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC) // Date is a date with day, month and year. Date = MustCreateDatetimeType(sqltypes.Date, 0) @@ -222,8 +223,8 @@ func ConvertToTime(ctx context.Context, v interface{}, t datetimeType) (time.Tim return time.Time{}, err } - if res.Equal(zeroTime) { - return zeroTime, nil + if res.Equal(ZeroTime) { + return ZeroTime, nil } // Round the date to the precision of this type @@ -275,13 +276,13 @@ func (t datetimeType) ConvertWithoutRangeCheck(ctx context.Context, v interface{ switch value := v.(type) { case string: if value == ZeroDateStr || value == ZeroTimestampDatetimeStr { - return zeroTime, nil + return ZeroTime, nil } // TODO: consider not using time.Parse if we want to match MySQL exactly ('2010-06-03 11:22.:.:.:.:' is a valid timestamp) var parsed bool res, parsed, err = parseDatetime(value) if !parsed { - return zeroTime, ErrConvertingToTime.New(v) + return ZeroTime, ErrConvertingToTime.New(v) } case time.Time: res = value.UTC() @@ -289,84 +290,89 @@ func (t datetimeType) ConvertWithoutRangeCheck(ctx context.Context, v interface{ // is zero values, which are important when converting from postgres defaults. case int: if value == 0 { - return zeroTime, nil + return ZeroTime, nil } - return zeroTime, ErrConvertingToTime.New(v) + return ZeroTime, ErrConvertingToTime.New(v) case int8: if value == 0 { - return zeroTime, nil + return ZeroTime, nil } - return zeroTime, ErrConvertingToTime.New(v) + return ZeroTime, ErrConvertingToTime.New(v) case int16: if value == 0 { - return zeroTime, nil + return ZeroTime, nil } - return zeroTime, ErrConvertingToTime.New(v) + return ZeroTime, ErrConvertingToTime.New(v) case int32: if value == 0 { - return zeroTime, nil + return ZeroTime, nil } - return zeroTime, ErrConvertingToTime.New(v) + return ZeroTime, ErrConvertingToTime.New(v) case int64: if value == 0 { - return zeroTime, nil + return ZeroTime, nil } - return zeroTime, ErrConvertingToTime.New(v) + return ZeroTime, ErrConvertingToTime.New(v) case uint: if value == 0 { - return zeroTime, nil + return ZeroTime, nil } - return zeroTime, ErrConvertingToTime.New(v) + return ZeroTime, ErrConvertingToTime.New(v) case uint8: if value == 0 { - return zeroTime, nil + return ZeroTime, nil } - return zeroTime, ErrConvertingToTime.New(v) + return ZeroTime, ErrConvertingToTime.New(v) case uint16: if value == 0 { - return zeroTime, nil + return ZeroTime, nil } - return zeroTime, ErrConvertingToTime.New(v) + return ZeroTime, ErrConvertingToTime.New(v) case uint32: if value == 0 { - return zeroTime, nil + return ZeroTime, nil } - return zeroTime, ErrConvertingToTime.New(v) + return ZeroTime, ErrConvertingToTime.New(v) case uint64: if value == 0 { - return zeroTime, nil + return ZeroTime, nil } - return zeroTime, ErrConvertingToTime.New(v) + return ZeroTime, ErrConvertingToTime.New(v) case float32: if value == 0 { - return zeroTime, nil + return ZeroTime, nil } - return zeroTime, ErrConvertingToTime.New(v) + return ZeroTime, ErrConvertingToTime.New(v) case float64: if value == 0 { - return zeroTime, nil + return ZeroTime, nil } - return zeroTime, ErrConvertingToTime.New(v) + return ZeroTime, ErrConvertingToTime.New(v) case decimal.Decimal: if value.IsZero() { - return zeroTime, nil + return ZeroTime, nil } - return zeroTime, ErrConvertingToTime.New(v) + return ZeroTime, ErrConvertingToTime.New(v) case decimal.NullDecimal: if value.Valid && value.Decimal.IsZero() { - return zeroTime, nil + return ZeroTime, nil } - return zeroTime, ErrConvertingToTime.New(v) + return ZeroTime, ErrConvertingToTime.New(v) case Timespan: // when receiving TIME, MySQL fills in date with today nowTimeStr := sql.Now().Format("2006-01-02") nowTime, err := time.Parse("2006-01-02", nowTimeStr) if err != nil { - return zeroTime, ErrConvertingToTime.New(v) + return ZeroTime, ErrConvertingToTime.New(v) } return nowTime.Add(value.AsTimeDuration()), nil + case bool: + if !value { + return ZeroTime, nil + } + return ZeroTime, ErrConvertingToTime.New(v) default: - return zeroTime, sql.ErrConvertToSQL.New(value, t) + return ZeroTime, sql.ErrConvertToSQL.New(value, t) } if t.baseType == sqltypes.Date { @@ -452,21 +458,21 @@ func (t datetimeType) SQL(ctx *sql.Context, dest []byte, v interface{}) (sqltype switch t.baseType { case sqltypes.Date: typ = sqltypes.Date - if vt.Equal(zeroTime) { + if vt.Equal(ZeroTime) { val = vt.AppendFormat(dest, ZeroDateStr) } else { val = vt.AppendFormat(dest, sql.DateLayout) } case sqltypes.Datetime: typ = sqltypes.Datetime - if vt.Equal(zeroTime) { + if vt.Equal(ZeroTime) { val = vt.AppendFormat(dest, ZeroTimestampDatetimeStr) } else { val = vt.AppendFormat(dest, sql.TimestampDatetimeLayout) } case sqltypes.Timestamp: typ = sqltypes.Timestamp - if vt.Equal(zeroTime) { + if vt.Equal(ZeroTime) { val = vt.AppendFormat(dest, ZeroTimestampDatetimeStr) } else { val = vt.AppendFormat(dest, sql.TimestampDatetimeLayout) @@ -529,7 +535,7 @@ func (t datetimeType) ValueType() reflect.Type { } func (t datetimeType) Zero() interface{} { - return zeroTime + return ZeroTime } // CollationCoercibility implements sql.CollationCoercible interface. diff --git a/sql/types/datetime_test.go b/sql/types/datetime_test.go index 6efc77af6c..b785ada21c 100644 --- a/sql/types/datetime_test.go +++ b/sql/types/datetime_test.go @@ -270,53 +270,53 @@ func TestDatetimeConvert(t *testing.T) { {Date, "", nil, true}, {Date, "0500-01-01", time.Date(500, 1, 1, 0, 0, 0, 0, time.UTC), false}, {Date, "10000-01-01", nil, true}, - {Date, int(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {Date, int8(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {Date, int16(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {Date, int32(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {Date, int64(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {Date, uint(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {Date, uint8(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {Date, uint16(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {Date, uint32(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {Date, uint64(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {Date, float32(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {Date, float64(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, + {Date, int(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {Date, int8(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {Date, int16(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {Date, int32(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {Date, int64(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {Date, uint(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {Date, uint8(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {Date, uint16(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {Date, uint32(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {Date, uint64(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {Date, float32(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {Date, float64(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, {Date, []byte{0}, nil, true}, {DatetimeMaxPrecision, "0500-01-01 01:01:01", time.Date(500, 1, 1, 1, 1, 1, 0, time.UTC), false}, {DatetimeMaxPrecision, "0000-01-01 00:00:00", time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, {DatetimeMaxPrecision, time.Date(10000, 1, 1, 1, 1, 1, 1, time.UTC), nil, true}, - {DatetimeMaxPrecision, int(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {DatetimeMaxPrecision, int8(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {DatetimeMaxPrecision, int16(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {DatetimeMaxPrecision, int32(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {DatetimeMaxPrecision, int64(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {DatetimeMaxPrecision, uint(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {DatetimeMaxPrecision, uint8(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {DatetimeMaxPrecision, uint16(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {DatetimeMaxPrecision, uint32(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {DatetimeMaxPrecision, uint64(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {DatetimeMaxPrecision, float32(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {DatetimeMaxPrecision, float64(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, + {DatetimeMaxPrecision, int(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {DatetimeMaxPrecision, int8(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {DatetimeMaxPrecision, int16(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {DatetimeMaxPrecision, int32(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {DatetimeMaxPrecision, int64(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {DatetimeMaxPrecision, uint(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {DatetimeMaxPrecision, uint8(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {DatetimeMaxPrecision, uint16(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {DatetimeMaxPrecision, uint32(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {DatetimeMaxPrecision, uint64(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {DatetimeMaxPrecision, float32(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {DatetimeMaxPrecision, float64(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, {DatetimeMaxPrecision, []byte{0}, nil, true}, {TimestampMaxPrecision, time.Date(1960, 1, 1, 1, 1, 1, 1, time.UTC), nil, true}, {TimestampMaxPrecision, "1970-01-01 00:00:00", nil, true}, {TimestampMaxPrecision, "1970-01-01 00:00:01", time.Date(1970, 1, 1, 0, 0, 1, 0, time.UTC), false}, {TimestampMaxPrecision, time.Date(2040, 1, 1, 1, 1, 1, 1, time.UTC), nil, true}, - {TimestampMaxPrecision, int(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {TimestampMaxPrecision, int8(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {TimestampMaxPrecision, int16(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {TimestampMaxPrecision, int32(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {TimestampMaxPrecision, int64(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {TimestampMaxPrecision, uint(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {TimestampMaxPrecision, uint8(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {TimestampMaxPrecision, uint16(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {TimestampMaxPrecision, uint32(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {TimestampMaxPrecision, uint64(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {TimestampMaxPrecision, float32(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, - {TimestampMaxPrecision, float64(0), time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), false}, + {TimestampMaxPrecision, int(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {TimestampMaxPrecision, int8(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {TimestampMaxPrecision, int16(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {TimestampMaxPrecision, int32(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {TimestampMaxPrecision, int64(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {TimestampMaxPrecision, uint(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {TimestampMaxPrecision, uint8(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {TimestampMaxPrecision, uint16(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {TimestampMaxPrecision, uint32(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {TimestampMaxPrecision, uint64(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {TimestampMaxPrecision, float32(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, + {TimestampMaxPrecision, float64(0), time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC), false}, {TimestampMaxPrecision, []byte{0}, nil, true}, {Date, int(1), nil, true},