Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

types,expression: Fix parse time from string #7654

Merged
merged 7 commits into from
Sep 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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