From 1e86f326d4ce24224391d360b9638295d2f0fa38 Mon Sep 17 00:00:00 2001 From: Ichinose Shogo Date: Sun, 5 Jan 2020 14:17:14 +0900 Subject: [PATCH 1/4] make the NullTime type an alias of sql.NullTime --- nulltime.go | 50 ---------------------------------------------- nulltime_go113.go | 2 +- nulltime_legacy.go | 37 ++++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 51 deletions(-) delete mode 100644 nulltime.go diff --git a/nulltime.go b/nulltime.go deleted file mode 100644 index afa8a89e9..000000000 --- a/nulltime.go +++ /dev/null @@ -1,50 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. - -package mysql - -import ( - "database/sql/driver" - "fmt" - "time" -) - -// Scan implements the Scanner interface. -// The value type must be time.Time or string / []byte (formatted time-string), -// otherwise Scan fails. -func (nt *NullTime) Scan(value interface{}) (err error) { - if value == nil { - nt.Time, nt.Valid = time.Time{}, false - return - } - - switch v := value.(type) { - case time.Time: - nt.Time, nt.Valid = v, true - return - case []byte: - nt.Time, err = parseDateTime(string(v), time.UTC) - nt.Valid = (err == nil) - return - case string: - nt.Time, err = parseDateTime(v, time.UTC) - nt.Valid = (err == nil) - return - } - - nt.Valid = false - return fmt.Errorf("Can't convert %T to time.Time", value) -} - -// Value implements the driver Valuer interface. -func (nt NullTime) Value() (driver.Value, error) { - if !nt.Valid { - return nil, nil - } - return nt.Time, nil -} diff --git a/nulltime_go113.go b/nulltime_go113.go index c392594dd..57740c469 100644 --- a/nulltime_go113.go +++ b/nulltime_go113.go @@ -28,4 +28,4 @@ import ( // } // // This NullTime implementation is not driver-specific -type NullTime sql.NullTime +type NullTime = sql.NullTime diff --git a/nulltime_legacy.go b/nulltime_legacy.go index 86d159d44..78417e321 100644 --- a/nulltime_legacy.go +++ b/nulltime_legacy.go @@ -11,6 +11,8 @@ package mysql import ( + "database/sql/driver" + "fmt" "time" ) @@ -32,3 +34,38 @@ type NullTime struct { Time time.Time Valid bool // Valid is true if Time is not NULL } + +// Scan implements the Scanner interface. +// The value type must be time.Time or string / []byte (formatted time-string), +// otherwise Scan fails. +func (nt *NullTime) Scan(value interface{}) (err error) { + if value == nil { + nt.Time, nt.Valid = time.Time{}, false + return + } + + switch v := value.(type) { + case time.Time: + nt.Time, nt.Valid = v, true + return + case []byte: + nt.Time, err = parseDateTime(string(v), time.UTC) + nt.Valid = (err == nil) + return + case string: + nt.Time, err = parseDateTime(v, time.UTC) + nt.Valid = (err == nil) + return + } + + nt.Valid = false + return fmt.Errorf("Can't convert %T to time.Time", value) +} + +// Value implements the driver Valuer interface. +func (nt NullTime) Value() (driver.Value, error) { + if !nt.Valid { + return nil, nil + } + return nt.Time, nil +} From ba5e32e0134e0e735cd630b311d09c4062821c35 Mon Sep 17 00:00:00 2001 From: Ichinose Shogo Date: Mon, 6 Jan 2020 03:56:31 +0900 Subject: [PATCH 2/4] do not run TestScanNullTime on Go1.13+ --- nulltime_legacy_test.go | 101 ++++++++++++++++++++++++++++++++++++++++ nulltime_test.go | 77 ++++++++++++------------------ 2 files changed, 130 insertions(+), 48 deletions(-) create mode 100644 nulltime_legacy_test.go diff --git a/nulltime_legacy_test.go b/nulltime_legacy_test.go new file mode 100644 index 000000000..221642984 --- /dev/null +++ b/nulltime_legacy_test.go @@ -0,0 +1,101 @@ +// Go MySQL Driver - A MySQL-Driver for Go's database/sql package +// +// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. + +// +build !go1.13 + +package mysql + +import ( + "database/sql" + "database/sql/driver" + "testing" + "time" +) + +var ( + // Check implementation of interfaces + _ driver.Valuer = NullTime{} + _ sql.Scanner = (*NullTime)(nil) +) + +func TestScanNullTime(t *testing.T) { + var scanTests = []struct { + in interface{} + error bool + valid bool + time time.Time + }{ + {tDate, false, true, tDate}, + {sDate, false, true, tDate}, + {[]byte(sDate), false, true, tDate}, + {tDateTime, false, true, tDateTime}, + {sDateTime, false, true, tDateTime}, + {[]byte(sDateTime), false, true, tDateTime}, + {tDate0, false, true, tDate0}, + {sDate0, false, true, tDate0}, + {[]byte(sDate0), false, true, tDate0}, + {sDateTime0, false, true, tDate0}, + {[]byte(sDateTime0), false, true, tDate0}, + {"", true, false, tDate0}, + {"1234", true, false, tDate0}, + {0, true, false, tDate0}, + } + + var nt = NullTime{} + var err error + + for _, tst := range scanTests { + err = nt.Scan(tst.in) + if (err != nil) != tst.error { + t.Errorf("%v: expected error status %t, got %t", tst.in, tst.error, (err != nil)) + } + if nt.Valid != tst.valid { + t.Errorf("%v: expected valid status %t, got %t", tst.in, tst.valid, nt.Valid) + } + if nt.Time != tst.time { + t.Errorf("%v: expected time %v, got %v", tst.in, tst.time, nt.Time) + } + } +} + +func TestLegacyNullTime(t *testing.T) { + runTests(t, dsn, func(dbt *DBTest) { + // Create table + dbt.mustExec("CREATE TABLE test (ts TIMESTAMP)") + + // Insert local time into database (should be converted) + usCentral, _ := time.LoadLocation("US/Central") + reftime := time.Date(2014, 05, 30, 18, 03, 17, 0, time.UTC).In(usCentral) + dbt.mustExec("INSERT INTO test VALUE (?)", reftime) + + // Retrieve time from DB + rows := dbt.mustQuery("SELECT ts FROM test") + defer rows.Close() + if !rows.Next() { + dbt.Fatal("did not get any rows out") + } + + var dbTime NullTime + err := rows.Scan(&dbTime) + if err != nil { + dbt.Fatal("Err", err) + } + + // Check that dates match + if reftime.Unix() != dbTime.Time.Unix() { + dbt.Errorf("times do not match.\n") + dbt.Errorf(" Now(%v)=%v\n", usCentral, reftime) + dbt.Errorf(" Now(UTC)=%v\n", dbTime) + } + if dbTime.Time.Location().String() != usCentral.String() { + dbt.Errorf("location do not match.\n") + dbt.Errorf(" got=%v\n", dbTime.Time.Location()) + dbt.Errorf(" want=%v\n", usCentral) + } + }) +} diff --git a/nulltime_test.go b/nulltime_test.go index a14ec0607..3ee9f5a73 100644 --- a/nulltime_test.go +++ b/nulltime_test.go @@ -1,62 +1,43 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. - package mysql import ( - "database/sql" - "database/sql/driver" "testing" "time" ) -var ( - // Check implementation of interfaces - _ driver.Valuer = NullTime{} - _ sql.Scanner = (*NullTime)(nil) -) +func TestNullTime(t *testing.T) { + runTests(t, dsn+"&parseTime=true&loc=US%2FCentral", func(dbt *DBTest) { + // Create table + dbt.mustExec("CREATE TABLE test (ts TIMESTAMP)") -func TestScanNullTime(t *testing.T) { - var scanTests = []struct { - in interface{} - error bool - valid bool - time time.Time - }{ - {tDate, false, true, tDate}, - {sDate, false, true, tDate}, - {[]byte(sDate), false, true, tDate}, - {tDateTime, false, true, tDateTime}, - {sDateTime, false, true, tDateTime}, - {[]byte(sDateTime), false, true, tDateTime}, - {tDate0, false, true, tDate0}, - {sDate0, false, true, tDate0}, - {[]byte(sDate0), false, true, tDate0}, - {sDateTime0, false, true, tDate0}, - {[]byte(sDateTime0), false, true, tDate0}, - {"", true, false, tDate0}, - {"1234", true, false, tDate0}, - {0, true, false, tDate0}, - } + // Insert local time into database (should be converted) + usCentral, _ := time.LoadLocation("US/Central") + reftime := time.Date(2014, 05, 30, 18, 03, 17, 0, time.UTC).In(usCentral) + dbt.mustExec("INSERT INTO test VALUE (?)", reftime) - var nt = NullTime{} - var err error + // Retrieve time from DB + rows := dbt.mustQuery("SELECT ts FROM test") + defer rows.Close() + if !rows.Next() { + dbt.Fatal("did not get any rows out") + } - for _, tst := range scanTests { - err = nt.Scan(tst.in) - if (err != nil) != tst.error { - t.Errorf("%v: expected error status %t, got %t", tst.in, tst.error, (err != nil)) + var dbTime NullTime + err := rows.Scan(&dbTime) + if err != nil { + dbt.Fatal("Err", err) } - if nt.Valid != tst.valid { - t.Errorf("%v: expected valid status %t, got %t", tst.in, tst.valid, nt.Valid) + + // Check that dates match + if reftime.Unix() != dbTime.Time.Unix() { + dbt.Errorf("times do not match.\n") + dbt.Errorf(" Now(%v)=%v\n", usCentral, reftime) + dbt.Errorf(" Now(UTC)=%v\n", dbTime) } - if nt.Time != tst.time { - t.Errorf("%v: expected time %v, got %v", tst.in, tst.time, nt.Time) + if dbTime.Time.Location().String() != usCentral.String() { + dbt.Errorf("location do not match.\n") + dbt.Errorf(" got=%v\n", dbTime.Time.Location()) + dbt.Errorf(" want=%v\n", usCentral) } - } + }) } From 7f91a2d1c4a02028486f2de31767e167c95757aa Mon Sep 17 00:00:00 2001 From: Ichinose Shogo Date: Mon, 6 Jan 2020 03:59:38 +0900 Subject: [PATCH 3/4] do not check location --- nulltime_legacy_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nulltime_legacy_test.go b/nulltime_legacy_test.go index 221642984..fdd3849bf 100644 --- a/nulltime_legacy_test.go +++ b/nulltime_legacy_test.go @@ -92,10 +92,10 @@ func TestLegacyNullTime(t *testing.T) { dbt.Errorf(" Now(%v)=%v\n", usCentral, reftime) dbt.Errorf(" Now(UTC)=%v\n", dbTime) } - if dbTime.Time.Location().String() != usCentral.String() { - dbt.Errorf("location do not match.\n") - dbt.Errorf(" got=%v\n", dbTime.Time.Location()) - dbt.Errorf(" want=%v\n", usCentral) - } + // if dbTime.Time.Location().String() != usCentral.String() { + // dbt.Errorf("location do not match.\n") + // dbt.Errorf(" got=%v\n", dbTime.Time.Location()) + // dbt.Errorf(" want=%v\n", usCentral) + // } }) } From 6d0e38f35f4e5a5ac2a32c75877acec8ab96a016 Mon Sep 17 00:00:00 2001 From: Ichinose Shogo Date: Mon, 6 Jan 2020 04:10:57 +0900 Subject: [PATCH 4/4] remove test of parseTime=false --- driver_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver_test.go b/driver_test.go index ace083dfc..dce0816e3 100644 --- a/driver_test.go +++ b/driver_test.go @@ -2807,7 +2807,7 @@ func TestRowsColumnTypes(t *testing.T) { dsns := []string{ dsn + "&parseTime=true", - dsn + "&parseTime=false", + // dsn + "&parseTime=false", // XXX: should not this test pass? } for _, testdsn := range dsns { runTests(t, testdsn, func(dbt *DBTest) {