-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement date, time and timestamp literals (#10921)
* Implement date, time and timestamp literals Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com> * feat: add a test that verifies we can use date, timestamp and time as column names Signed-off-by: Manan Gupta <manan@planetscale.com> * feat: add precedence rule for non-reserved-keywords that can also be used to type cast STRINGS Signed-off-by: Manan Gupta <manan@planetscale.com> * test: fix tpch test to not expect syntax error Signed-off-by: Manan Gupta <manan@planetscale.com> * feat: make parser Signed-off-by: Manan Gupta <manan@planetscale.com> * Add validate of date style literals This adds additional validation of date, time and timestamp literals. It uses the same validation the evalengine already uses today which is more restricted than MySQL itself but seems good enough for now. It consolidates the parsing into one place so if the syntax allowed is extended, we only have to update one location. Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com> * Implement more complete TIME parsing support Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com> Co-authored-by: Manan Gupta <manan@planetscale.com>
- Loading branch information
1 parent
6d68fd5
commit 6450e49
Showing
20 changed files
with
7,356 additions
and
6,818 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
/* | ||
Copyright 2019 The Vitess Authors. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package sqlparser | ||
|
||
import ( | ||
"fmt" | ||
"strconv" | ||
"strings" | ||
"time" | ||
|
||
vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" | ||
"vitess.io/vitess/go/vt/vterrors" | ||
) | ||
|
||
var dateFormats = []string{"2006-01-02", "06-01-02", "20060102", "060102"} | ||
var datetimeFormats = []string{"2006-01-02 15:04:05.9", "06-01-02 15:04:05.9", "20060102150405.9", "060102150405.9"} | ||
var timeWithDayFormats = []string{"15:04:05.9", "15:04", "15"} | ||
var timeWithoutDayFormats = []string{"15:04:05.9", "15:04", "150405.9", "0405", "05"} | ||
|
||
func ParseDate(in string) (t time.Time, err error) { | ||
for _, f := range dateFormats { | ||
t, err = time.Parse(f, in) | ||
if err == nil { | ||
return t, nil | ||
} | ||
} | ||
return t, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.WrongValue, "incorrect DATE value: '%s'", in) | ||
} | ||
|
||
func ParseTime(in string) (t time.Time, err error) { | ||
// ParseTime is right now only excepting on specific | ||
// time format and doesn't accept all formats MySQL accepts. | ||
// Can be improved in the future as needed. | ||
if in == "" { | ||
return t, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.WrongValue, "incorrect TIME value: '%s'", in) | ||
} | ||
start := 0 | ||
neg := in[start] == '-' | ||
if neg { | ||
start++ | ||
} | ||
|
||
parts := strings.Split(in[start:], " ") | ||
if len(parts) > 2 { | ||
return t, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.WrongValue, "incorrect TIME value: '%s'", in) | ||
} | ||
days := 0 | ||
hourMinuteSeconds := parts[0] | ||
if len(parts) == 2 { | ||
days, err = strconv.Atoi(parts[0]) | ||
if err != nil { | ||
fmt.Printf("atoi failed: %+v\n", err) | ||
return t, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.WrongValue, "incorrect TIME value: '%s'", in) | ||
} | ||
if days < 0 { | ||
// Double negative which is not allowed | ||
return t, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.WrongValue, "incorrect TIME value: '%s'", in) | ||
} | ||
if days > 34 { | ||
return t, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.WrongValue, "incorrect TIME value: '%s'", in) | ||
} | ||
for _, f := range timeWithDayFormats { | ||
t, err = time.Parse(f, parts[1]) | ||
if err == nil { | ||
break | ||
} | ||
} | ||
} else { | ||
for _, f := range timeWithoutDayFormats { | ||
t, err = time.Parse(f, hourMinuteSeconds) | ||
if err == nil { | ||
break | ||
} | ||
} | ||
} | ||
|
||
if err != nil { | ||
return t, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.WrongValue, "incorrect TIME value: '%s'", in) | ||
} | ||
|
||
// setting the date to today's date, because t is "0000-01-01 xx:xx:xx" | ||
now := time.Now() | ||
year, month, day := now.Date() | ||
if neg { | ||
// If we have a negative time, we start with the start of today | ||
// and substract the total duration of the parsed time. | ||
today := time.Date(year, month, day, 0, 0, 0, 0, t.Location()) | ||
duration := time.Duration(days)*24*time.Hour + | ||
time.Duration(t.Hour())*time.Hour + | ||
time.Duration(t.Minute())*time.Minute + | ||
time.Duration(t.Second())*time.Second + | ||
time.Duration(t.Nanosecond())*time.Nanosecond | ||
t = today.Add(-duration) | ||
} else { | ||
// In case of a positive time, we can take a quicker | ||
// shortcut and add the date of today. | ||
t = t.AddDate(year, int(month-1), day-1+days) | ||
} | ||
return t, nil | ||
} | ||
|
||
func ParseDateTime(in string) (t time.Time, err error) { | ||
for _, f := range datetimeFormats { | ||
t, err = time.Parse(f, in) | ||
if err == nil { | ||
return t, nil | ||
} | ||
} | ||
return t, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.WrongValue, "incorrect DATETIME value: '%s'", in) | ||
} |
Oops, something went wrong.