Skip to content

Commit

Permalink
types,expression: Fix parse time from string (#7654)
Browse files Browse the repository at this point in the history
  • Loading branch information
spongedu authored and coocood committed Sep 11, 2018
1 parent e0de9ca commit 5ad005b
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 17 deletions.
36 changes: 24 additions & 12 deletions expression/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1918,6 +1918,18 @@ func (s *testIntegrationSuite) TestBuiltin(c *C) {
result = tk.MustQuery(`select cast(20170118.999 as datetime);`)
result.Check(testkit.Rows("2017-01-18 00:00:00"))

result = tk.MustQuery(`select cast("170102034" as datetime);`)
result.Check(testkit.Rows("2017-01-02 03:04:00"))
result = tk.MustQuery(`select cast("1701020304" as datetime);`)
result.Check(testkit.Rows("2017-01-02 03:04:00"))
result = tk.MustQuery(`select cast("1701020304." as datetime);`)
result.Check(testkit.Rows("2017-01-02 03:04:00"))
result = tk.MustQuery(`select cast("1701020304.1" as datetime);`)
result.Check(testkit.Rows("2017-01-02 03:04:01"))
result = tk.MustQuery(`select cast("1701020304.111" as datetime);`)
result.Check(testkit.Rows("2017-01-02 03:04:11"))
tk.MustQuery("show warnings;").Check(testkit.Rows("Warning 1292 Truncated incorrect datetime value: '1701020304.111'"))

// for ISNULL
tk.MustExec("drop table if exists t")
tk.MustExec("create table t (a int, b int, c int, d char(10), e datetime, f float, g decimal(10, 3))")
Expand Down Expand Up @@ -3257,32 +3269,32 @@ func (s *testIntegrationSuite) TestFuncJSON(c *C) {
c.Assert(err, NotNil)
c.Assert(err.Error(), Equals, "[json:3149]In this situation, path expressions may not contain the * and ** tokens.")

r = tk.MustQuery(`select
json_contains_path(NULL, 'one', "$.c"),
json_contains_path(NULL, 'all', "$.c"),
json_contains_path('{"a": 1}', NULL, "$.c"),
json_contains_path('{"a": 1}', 'one', NULL),
r = tk.MustQuery(`select
json_contains_path(NULL, 'one', "$.c"),
json_contains_path(NULL, 'all', "$.c"),
json_contains_path('{"a": 1}', NULL, "$.c"),
json_contains_path('{"a": 1}', 'one', NULL),
json_contains_path('{"a": 1}', 'all', NULL)
`)
r.Check(testkit.Rows("<nil> <nil> <nil> <nil> <nil>"))

r = tk.MustQuery(`select
json_contains_path('{"a": 1, "b": 2, "c": {"d": 4}}', 'one', '$.c.d'),
r = tk.MustQuery(`select
json_contains_path('{"a": 1, "b": 2, "c": {"d": 4}}', 'one', '$.c.d'),
json_contains_path('{"a": 1, "b": 2, "c": {"d": 4}}', 'one', '$.a.d'),
json_contains_path('{"a": 1, "b": 2, "c": {"d": 4}}', 'all', '$.c.d'),
json_contains_path('{"a": 1, "b": 2, "c": {"d": 4}}', 'all', '$.c.d'),
json_contains_path('{"a": 1, "b": 2, "c": {"d": 4}}', 'all', '$.a.d')
`)
r.Check(testkit.Rows("1 0 1 0"))

r = tk.MustQuery(`select
json_contains_path('{"a": 1, "b": 2, "c": {"d": 4}}', 'one', '$.a', '$.e'),
r = tk.MustQuery(`select
json_contains_path('{"a": 1, "b": 2, "c": {"d": 4}}', 'one', '$.a', '$.e'),
json_contains_path('{"a": 1, "b": 2, "c": {"d": 4}}', 'one', '$.a', '$.b'),
json_contains_path('{"a": 1, "b": 2, "c": {"d": 4}}', 'all', '$.a', '$.e'),
json_contains_path('{"a": 1, "b": 2, "c": {"d": 4}}', 'all', '$.a', '$.e'),
json_contains_path('{"a": 1, "b": 2, "c": {"d": 4}}', 'all', '$.a', '$.b')
`)
r.Check(testkit.Rows("1 1 0 1"))

r = tk.MustQuery(`select
r = tk.MustQuery(`select
json_contains_path('{"a": 1, "b": 2, "c": {"d": 4}}', 'one', '$.*'),
json_contains_path('{"a": 1, "b": 2, "c": {"d": 4}}', 'one', '$[*]'),
json_contains_path('{"a": 1, "b": 2, "c": {"d": 4}}', 'all', '$.*'),
Expand Down
30 changes: 25 additions & 5 deletions types/time.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ var (
ErrInvalidYear = errors.New("invalid year")
ErrZeroDate = errors.New("datetime zero in date")
ErrIncorrectDatetimeValue = terror.ClassTypes.New(mysql.ErrTruncatedWrongValue, "Incorrect datetime value: '%s'")
ErrTruncatedWrongValue = terror.ClassTypes.New(mysql.ErrTruncatedWrongValue, mysql.MySQLErrName[mysql.ErrTruncatedWrongValue])
)

// Time format without fractional seconds precision.
Expand Down Expand Up @@ -600,7 +601,7 @@ func splitDateTime(format string) (seps []string, fracStr string) {
}

// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-literals.html.
func parseDatetime(str string, fsp int, isFloat bool) (Time, error) {
func parseDatetime(sc *stmtctx.StatementContext, str string, fsp int, isFloat bool) (Time, error) {
// Try to split str with delimiter.
// TODO: only punctuation can be the delimiter for date parts or time parts.
// But only space and T can be the delimiter between the date and time part.
Expand All @@ -614,8 +615,8 @@ func parseDatetime(str string, fsp int, isFloat bool) (Time, error) {
seps, fracStr := splitDateTime(str)
switch len(seps) {
case 1:
len := len(seps[0])
switch len {
l := len(seps[0])
switch l {
case 14: // No delimiter.
// YYYYMMDDHHMMSS
_, err = fmt.Sscanf(seps[0], "%4d%2d%2d%2d%2d%2d", &year, &month, &day, &hour, &minute, &second)
Expand All @@ -628,6 +629,12 @@ func parseDatetime(str string, fsp int, isFloat bool) (Time, error) {
_, err = fmt.Sscanf(seps[0], "%2d%2d%2d%2d%2d%1d", &year, &month, &day, &hour, &minute, &second)
year = adjustYear(year)
hhmmss = true
case 10: // YYMMDDHHMM
_, err = fmt.Sscanf(seps[0], "%2d%2d%2d%2d%2d", &year, &month, &day, &hour, &minute)
year = adjustYear(year)
case 9: // YYMMDDHHM
_, err = fmt.Sscanf(seps[0], "%2d%2d%2d%2d%1d", &year, &month, &day, &hour, &minute)
year = adjustYear(year)
case 8: // YYYYMMDD
_, err = fmt.Sscanf(seps[0], "%4d%2d%2d", &year, &month, &day)
case 6:
Expand All @@ -637,7 +644,7 @@ func parseDatetime(str string, fsp int, isFloat bool) (Time, error) {
default:
return ZeroDatetime, errors.Trace(ErrInvalidTimeFormat.GenByArgs(str))
}
if len == 6 || len == 8 {
if l == 6 || l == 8 {
// YYMMDD or YYYYMMDD
// We must handle float => string => datetime, the difference is that fractional
// part of float type is discarded directly, while fractional part of string type
Expand All @@ -649,6 +656,19 @@ func parseDatetime(str string, fsp int, isFloat bool) (Time, error) {
fmt.Sscanf(fracStr, "%2d%2d%2d", &hour, &minute, &second)
}
}
if l == 9 || l == 10 {
switch len(fracStr) {
case 0:
second = 0
case 1:
_, err = fmt.Sscanf(fracStr, "%1d", &second)
case 2:
_, err = fmt.Sscanf(fracStr, "%2d", &second)
default:
_, err = fmt.Sscanf(fracStr[:2], "%2d", &second)
sc.AppendWarning(ErrTruncatedWrongValue.GenByArgs("datetime", str))
}
}
case 3:
// YYYY-MM-DD
err = scanTimeArgs(seps, &year, &month, &day)
Expand Down Expand Up @@ -1225,7 +1245,7 @@ func parseTime(sc *stmtctx.StatementContext, str string, tp byte, fsp int, isFlo
return Time{Time: ZeroTime, Type: tp}, errors.Trace(err)
}

t, err := parseDatetime(str, fsp, isFloat)
t, err := parseDatetime(sc, str, fsp, isFloat)
if err != nil {
return Time{Time: ZeroTime, Type: tp}, errors.Trace(err)
}
Expand Down
6 changes: 6 additions & 0 deletions types/time_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ func (s *testTimeSuite) TestDateTime(c *C) {
{"00-00-00", "0000-00-00 00:00:00"},
{"00-00-00 00:00:00.123", "2000-00-00 00:00:00.123"},
{"11111111111", "2011-11-11 11:11:01"},
{"1701020301.", "2017-01-02 03:01:00"},
{"1701020304.1", "2017-01-02 03:04:01.0"},
{"1701020302.11", "2017-01-02 03:02:11.00"},
{"170102036", "2017-01-02 03:06:00"},
{"170102039.", "2017-01-02 03:09:00"},
{"170102037.11", "2017-01-02 03:07:11.00"},
}

for _, test := range table {
Expand Down

0 comments on commit 5ad005b

Please sign in to comment.