Repository: pingcap/tidb (Go)

Issue:
Converting `decimal` to `datetime` or `timestamp` when insert is not compatible with MySQL
## Bug Report

Please answer these questions before submitting your issue. Thanks!

1. What did you do?
If possible, provide a recipe for reproducing the error.
```
CREATE TABLE t1 (a DATETIME(6));
INSERT INTO t1 VALUES (20010101100000.1234567);
CREATE TABLE t2(a TIMESTAMP);
INSERT INTO t2 VALUES (20010101100000.1234567);
```

2. What did you expect to see?
In MySQL
```
mysql> CREATE TABLE t1 (a DATETIME(6));
Query OK, 0 rows affected (0.03 sec)

mysql> INSERT INTO t1 VALUES (20010101100000.1234567);
Query OK, 1 row affected (0.00 sec)

mysql> CREATE TABLE t2(a TIMESTAMP);
Query OK, 0 rows affected (0.01 sec)

mysql> INSERT INTO t2 VALUES (20010101100000.1234567);
Query OK, 1 row affected (0.00 sec)
```


3. What did you see instead?
In TiDB
```
mysql> CREATE TABLE t1 (a DATETIME(6));
Query OK, 0 rows affected (0.02 sec)

mysql> INSERT INTO t1 VALUES (20010101100000.1234567);
ERROR 1105 (HY000): cannot convert datum from decimal to type datetime.

mysql> CREATE TABLE t2(a TIMESTAMP);
Query OK, 0 rows affected (0.01 sec)

mysql> INSERT INTO t2 VALUES (20010101100000.1234567);
ERROR 1105 (HY000): cannot convert datum from decimal to type timestamp.
```


4. What version of TiDB are you using (`tidb-server -V` or run `select tidb_version();` on TiDB)?
```
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| tidb_version()                                                                                                                                                                                                                                                                                                                           |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Release Version: v3.0.0-beta-227-g06183393e-dirty
Git Commit Hash: 06183393ed536aa248248b72d4bf9f5010947f66
Git Branch: master
UTC Build Time: 2019-03-15 08:22:53
GoVersion: go version go1.12 darwin/amd64
Race Enabled: false
TiKV Min Version: 2.1.0-alpha.1-ff3dd160846b7d1aed9079c389fc188f7f5ea13e
Check Table Before Drop: false |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
```




Discussion:
hi @qw4990 ,i am new fish of golang & infrastructure software. 
I fixed this issue in my local and tests was passed, but i have a question.
I know tidb is very take care performance, does convert decimal to string and then parse to DateTime is the best way?

Thanks for your contribution. @Debiancc 
Performance is important for TiDB, but correctness and compatibility are also important too.
Maybe we can do it right now, and optimize it later.



Patch:
diff --git a/types/datum.go b/types/datum.go
index 06a7a8c9dc3bf..92bfb65ec86c4 100644
--- a/types/datum.go
+++ b/types/datum.go
@@ -961,6 +961,8 @@ func (d *Datum) convertToMysqlTimestamp(sc *stmtctx.StatementContext, target *Fi
 		t, err = ParseTime(sc, d.GetString(), mysql.TypeTimestamp, fsp)
 	case KindInt64:
 		t, err = ParseTimeFromNum(sc, d.GetInt64(), mysql.TypeTimestamp, fsp)
+	case KindMysqlDecimal:
+		t, err = ParseTimeFromFloatString(sc, d.GetMysqlDecimal().String(), mysql.TypeTimestamp, fsp)
 	default:
 		return invalidConv(d, mysql.TypeTimestamp)
 	}
@@ -998,6 +1000,8 @@ func (d *Datum) convertToMysqlTime(sc *stmtctx.StatementContext, target *FieldTy
 			return ret, errors.Trace(err)
 		}
 		t, err = t.RoundFrac(sc, fsp)
+	case KindMysqlDecimal:
+		t, err = ParseTimeFromFloatString(sc, d.GetMysqlDecimal().String(), tp, fsp)
 	case KindString, KindBytes:
 		t, err = ParseTime(sc, d.GetString(), tp, fsp)
 	case KindInt64:


Test patch:
diff --git a/expression/integration_test.go b/expression/integration_test.go
index 973c6e9973e3d..8f4a4718fe204 100644
--- a/expression/integration_test.go
+++ b/expression/integration_test.go
@@ -4116,3 +4116,15 @@ func (s *testIntegrationSuite) TestIssue9710(c *C) {
 		break
 	}
 }
+
+// for issue #9770
+func (s *testIntegrationSuite) TestDecimalConvertToTime(c *C) {
+	tk := testkit.NewTestKit(c, s.store)
+	defer s.cleanEnv(c)
+
+	tk.MustExec("use test")
+	tk.MustExec("drop table if exists t")
+	tk.MustExec("create table t(a datetime(6), b timestamp)")
+	tk.MustExec("insert t values (20010101100000.123456, 20110707101112.123456)")
+	tk.MustQuery("select * from t").Check(testkit.Rows("2001-01-01 10:00:00.123456 2011-07-07 10:11:12"))
+}
diff --git a/types/convert_test.go b/types/convert_test.go
index f6487f980bfe7..6398b9b87eeac 100644
--- a/types/convert_test.go
+++ b/types/convert_test.go
@@ -630,7 +630,9 @@ func (s *testTypeConvertSuite) TestConvert(c *C) {
 	signedAccept(c, mysql.TypeDatetime, "2012-08-23 12:34:03.123456", "2012-08-23 12:34:03")
 	signedAccept(c, mysql.TypeDatetime, ZeroDatetime, "0000-00-00 00:00:00")
 	signedAccept(c, mysql.TypeDatetime, int64(0), "0000-00-00 00:00:00")
+	signedAccept(c, mysql.TypeDatetime, NewDecFromFloatForTest(20010101100000.123456), "2001-01-01 10:00:00")
 	signedAccept(c, mysql.TypeTimestamp, "2012-08-23 12:34:03.123456", "2012-08-23 12:34:03")
+	signedAccept(c, mysql.TypeTimestamp, NewDecFromFloatForTest(20010101100000.123456), "2001-01-01 10:00:00")
 	signedAccept(c, mysql.TypeDuration, "10:11:12", "10:11:12")
 	signedAccept(c, mysql.TypeDuration, ZeroDatetime, "00:00:00")
 	signedAccept(c, mysql.TypeDuration, ZeroDuration, "00:00:00")