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

sql: convert dates outside of supported time range to correct date #694

Closed
Closed
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ To be able to create your own data source implementation you need to implement t
- `sql.PushdownProjectionAndFiltersTable` interface will provide the same functionality described before, but also will push down the filters used in the executed query. It allows to filter data in advance, and speed up queries.
- `sql.Indexable` add index capabilities to your table. By implementing this interface you can create and use indexes on this table.
- `sql.Inserter` can be implemented if your data source tables allow insertions.
**IMPORTANT:** when you implement your tables, your columns of timestamp and datetime types **must** return dates in the following range: `1000-01-01 00:00:00` to `9999-12-31 23:59:59`. To do so, you can use the builtin function `sql.ToSupportedTimeRange`.

- If you need some custom tree modifications, you can also implement your own `analyzer.Rules`.

Expand Down
32 changes: 26 additions & 6 deletions sql/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,11 @@ var TimestampLayouts = []string{
"20060102",
}

var (
maxTimestamp = time.Date(9999, time.December, 31, 23, 59, 59, 0, time.UTC)
minTimestamp = time.Date(1000, time.January, 1, 0, 0, 0, 0, time.UTC)
)

// SQL implements Type interface.
func (t timestampT) SQL(v interface{}) sqltypes.Value {
if _, ok := v.(nullT); ok {
Expand All @@ -422,11 +427,25 @@ func (t timestampT) SQL(v interface{}) sqltypes.Value {
)
}

// ToSupportedTimeRange returns the time if it's within the supported datetime
// range or either the lower or upper bound.
func ToSupportedTimeRange(t time.Time) time.Time {
if t.Before(minTimestamp) {
return minTimestamp
}

if t.After(maxTimestamp) {
return maxTimestamp
}

return t
}

// Convert implements Type interface.
func (t timestampT) Convert(v interface{}) (interface{}, error) {
switch value := v.(type) {
case time.Time:
return value.UTC(), nil
return ToSupportedTimeRange(value.UTC()), nil
case string:
t, err := time.Parse(TimestampLayout, value)
if err != nil {
Expand All @@ -443,14 +462,14 @@ func (t timestampT) Convert(v interface{}) (interface{}, error) {
return nil, ErrConvertingToTime.Wrap(err, v)
}
}
return t.UTC(), nil
return ToSupportedTimeRange(t.UTC()), nil
default:
ts, err := Int64.Convert(v)
if err != nil {
return nil, ErrInvalidType.New(reflect.TypeOf(v))
}

return time.Unix(ts.(int64), 0).UTC(), nil
return ToSupportedTimeRange(time.Unix(ts.(int64), 0).UTC()), nil
}
}

Expand Down Expand Up @@ -497,20 +516,21 @@ func (t dateT) SQL(v interface{}) sqltypes.Value {
func (t dateT) Convert(v interface{}) (interface{}, error) {
switch value := v.(type) {
case time.Time:
return truncateDate(value).UTC(), nil
return truncateDate(ToSupportedTimeRange(value.UTC())), nil
case string:
t, err := time.Parse(DateLayout, value)
if err != nil {
return nil, ErrConvertingToTime.Wrap(err, v)
}
return truncateDate(t).UTC(), nil
return truncateDate(ToSupportedTimeRange(t.UTC())), nil
default:
ts, err := Int64.Convert(v)
if err != nil {
return nil, ErrInvalidType.New(reflect.TypeOf(v))
}

return truncateDate(time.Unix(ts.(int64), 0)).UTC(), nil
t := time.Unix(ts.(int64), 0)
return truncateDate(ToSupportedTimeRange(t.UTC())), nil
}
}

Expand Down