Skip to content
This repository was archived by the owner on Jan 28, 2021. It is now read-only.

Commit 70c0f52

Browse files
Add DATETIME type support
Signed-off-by: Alejandro García Montoro <[email protected]>
1 parent 98c7c11 commit 70c0f52

File tree

5 files changed

+107
-20
lines changed

5 files changed

+107
-20
lines changed

_integration/go/mysql_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func TestGrafana(t *testing.T) {
8282
{"name", "TEXT"},
8383
{"email", "TEXT"},
8484
{"phone_numbers", "JSON"},
85-
{"created_at", "DATETIME"},
85+
{"created_at", "TIMESTAMP"},
8686
},
8787
},
8888
{

engine_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1994,7 +1994,7 @@ func TestDDL(t *testing.T) {
19941994
testQuery(t, e,
19951995
"CREATE TABLE t1(a INTEGER, b TEXT, c DATE, "+
19961996
"d TIMESTAMP, e VARCHAR(20), f BLOB NOT NULL, "+
1997-
"b1 BOOL, b2 BOOLEAN NOT NULL)",
1997+
"b1 BOOL, b2 BOOLEAN NOT NULL, g DATETIME",
19981998
[]sql.Row(nil),
19991999
)
20002000

@@ -2013,6 +2013,7 @@ func TestDDL(t *testing.T) {
20132013
{Name: "f", Type: sql.Blob, Source: "t1"},
20142014
{Name: "b1", Type: sql.Uint8, Nullable: true, Source: "t1"},
20152015
{Name: "b2", Type: sql.Uint8, Source: "t1"},
2016+
{Name: "g", Type: sql.Datetime, Nullable: true, Source: "t1"},
20162017
}
20172018

20182019
require.Equal(s, testTable.Schema())

sql/parse/parse_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
)
1414

1515
var fixtures = map[string]sql.Node{
16-
`CREATE TABLE t1(a INTEGER, b TEXT, c DATE, d TIMESTAMP, e VARCHAR(20), f BLOB NOT NULL)`: plan.NewCreateTable(
16+
`CREATE TABLE t1(a INTEGER, b TEXT, c DATE, d TIMESTAMP, e VARCHAR(20), f BLOB NOT NULL, g DATETIME)`: plan.NewCreateTable(
1717
sql.UnresolvedDatabase(""),
1818
"t1",
1919
sql.Schema{{
@@ -40,6 +40,10 @@ var fixtures = map[string]sql.Node{
4040
Name: "f",
4141
Type: sql.Blob,
4242
Nullable: false,
43+
}, {
44+
Name: "g",
45+
Type: sql.Datetime,
46+
Nullable: true,
4347
}},
4448
),
4549
`DESCRIBE TABLE foo;`: plan.NewDescribe(

sql/type.go

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,8 @@ var (
198198
Timestamp timestampT
199199
// Date is a date with day, month and year.
200200
Date dateT
201+
// Datetime is a date and a time
202+
Datetime datetimeT
201203
// Text is a string type.
202204
Text textT
203205
// Boolean is a boolean type.
@@ -258,6 +260,8 @@ func MysqlTypeToType(sql query.Type) (Type, error) {
258260
// Since we can't get the size of the sqltypes.VarChar to instantiate a
259261
// specific VarChar(length) type we return a Text here
260262
return Text, nil
263+
case sqltypes.Datetime:
264+
return Datetime, nil
261265
case sqltypes.Bit:
262266
return Boolean, nil
263267
case sqltypes.TypeJSON:
@@ -597,6 +601,65 @@ func (t dateT) Compare(a, b interface{}) (int, error) {
597601
return 0, nil
598602
}
599603

604+
type datetimeT struct{}
605+
606+
// DatetimeLayout is the layout of the MySQL date format in the representation
607+
// Go understands.
608+
const DatetimeLayout = "2006-01-02 15:04:05"
609+
610+
func (t datetimeT) String() string { return "DATETIME" }
611+
612+
func (t datetimeT) Type() query.Type {
613+
return sqltypes.Datetime
614+
}
615+
616+
func (t datetimeT) SQL(v interface{}) (sqltypes.Value, error) {
617+
if v == nil {
618+
return sqltypes.NULL, nil
619+
}
620+
621+
v, err := t.Convert(v)
622+
if err != nil {
623+
return sqltypes.Value{}, err
624+
}
625+
626+
return sqltypes.MakeTrusted(
627+
sqltypes.Datetime,
628+
[]byte(v.(time.Time).Format(DatetimeLayout)),
629+
), nil
630+
}
631+
632+
func (t datetimeT) Convert(v interface{}) (interface{}, error) {
633+
switch value := v.(type) {
634+
case time.Time:
635+
return value.UTC(), nil
636+
case string:
637+
t, err := time.Parse(DatetimeLayout, value)
638+
if err != nil {
639+
return nil, ErrConvertingToTime.Wrap(err, v)
640+
}
641+
return t.UTC(), nil
642+
default:
643+
ts, err := Int64.Convert(v)
644+
if err != nil {
645+
return nil, ErrInvalidType.New(reflect.TypeOf(v))
646+
}
647+
648+
return time.Unix(ts.(int64), 0).UTC(), nil
649+
}
650+
}
651+
652+
func (t datetimeT) Compare(a, b interface{}) (int, error) {
653+
av := a.(time.Time)
654+
bv := b.(time.Time)
655+
if av.Before(bv) {
656+
return -1, nil
657+
} else if av.After(bv) {
658+
return 1, nil
659+
}
660+
return 0, nil
661+
}
662+
600663
type varCharT struct {
601664
length int
602665
}
@@ -1013,9 +1076,9 @@ func IsInteger(t Type) bool {
10131076
return IsSigned(t) || IsUnsigned(t)
10141077
}
10151078

1016-
// IsTime checks if t is a timestamp or date.
1079+
// IsTime checks if t is a timestamp, date or datetime
10171080
func IsTime(t Type) bool {
1018-
return t == Timestamp || t == Date
1081+
return t == Timestamp || t == Date || t == Datetime
10191082
}
10201083

10211084
// IsDecimal checks if t is decimal type.
@@ -1082,6 +1145,8 @@ func MySQLTypeName(t Type) string {
10821145
case sqltypes.Float64:
10831146
return "DOUBLE"
10841147
case sqltypes.Timestamp:
1148+
return "TIMESTAMP"
1149+
case sqltypes.Datetime:
10851150
return "DATETIME"
10861151
case sqltypes.Date:
10871152
return "DATE"

sql/type_test.go

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -236,40 +236,57 @@ func TestExtraTimestamps(t *testing.T) {
236236
}
237237
}
238238

239-
func TestDate(t *testing.T) {
239+
// Generic tests for Date and Datetime.
240+
// typ should be Date or Datetime
241+
func commonTestsDatesTypes(typ Type, layout string, t *testing.T) {
240242
require := require.New(t)
241243
now := time.Now().UTC()
242-
v, err := Date.Convert(now)
244+
v, err := typ.Convert(now)
243245
require.NoError(err)
244-
require.Equal(now.Format(DateLayout), v.(time.Time).Format(DateLayout))
246+
require.Equal(now.Format(layout), v.(time.Time).Format(layout))
245247

246-
v, err = Date.Convert(now.Format(DateLayout))
248+
v, err = typ.Convert(now.Format(layout))
247249
require.NoError(err)
248250
require.Equal(
249-
now.Format(DateLayout),
250-
v.(time.Time).Format(DateLayout),
251+
now.Format(layout),
252+
v.(time.Time).Format(layout),
251253
)
252254

253-
v, err = Date.Convert(now.Unix())
255+
v, err = typ.Convert(now.Unix())
254256
require.NoError(err)
255257
require.Equal(
256-
now.Format(DateLayout),
257-
v.(time.Time).Format(DateLayout),
258+
now.Format(layout),
259+
v.(time.Time).Format(layout),
258260
)
259261

260-
sql, err := Date.SQL(now)
262+
sql, err := typ.SQL(now)
261263
require.NoError(err)
262-
require.Equal([]byte(now.Format(DateLayout)), sql.Raw())
264+
require.Equal([]byte(now.Format(layout)), sql.Raw())
265+
266+
after := now.Add(26 * time.Hour)
267+
lt(t, typ, now, after)
268+
eq(t, typ, now, now)
269+
gt(t, typ, after, now)
270+
}
271+
272+
func TestDate(t *testing.T) {
273+
commonTestsDatesTypes(Date, DateLayout, t)
263274

275+
now := time.Now().UTC()
264276
after := now.Add(time.Second)
265277
eq(t, Date, now, after)
266278
eq(t, Date, now, now)
267279
eq(t, Date, after, now)
280+
}
268281

269-
after = now.Add(26 * time.Hour)
270-
lt(t, Date, now, after)
271-
eq(t, Date, now, now)
272-
gt(t, Date, after, now)
282+
func TestDatetime(t *testing.T) {
283+
commonTestsDatesTypes(Datetime, DatetimeLayout, t)
284+
285+
now := time.Now().UTC()
286+
after := now.Add(time.Millisecond)
287+
lt(t, Datetime, now, after)
288+
eq(t, Datetime, now, now)
289+
gt(t, Datetime, after, now)
273290
}
274291

275292
func TestBlob(t *testing.T) {

0 commit comments

Comments
 (0)