@@ -37,6 +37,8 @@ pub(crate) fn parse_interval_expression(
3737 Ok ( Box :: new ( IntervalExpr :: new ( interval) ) )
3838}
3939
40+ /// Parse string intl Interval expression.
41+ /// Ref: https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-INTERVAL-INPUT
4042fn parse_interval_literal (
4143 interval_str : & str ,
4244 location : SourceLocation ,
@@ -54,7 +56,8 @@ fn parse_interval_literal(
5456 let mut has_years = false ;
5557 let mut has_months = false ;
5658 let mut has_days = false ;
57- let mut has_hours = false ;
59+ let mut has_any_time_part = false ;
60+ let mut has_hours: bool = false ;
5861 let mut has_minutes = false ;
5962 let mut has_seconds = false ;
6063
@@ -79,28 +82,38 @@ fn parse_interval_literal(
7982 }
8083
8184 // Parse the unit
82- let maybe_unit = tokens[ position] ;
83- if matches ! ( maybe_unit, "year" | "years" ) {
85+ let mut maybe_unit = tokens[ position] ;
86+ let unit_lower = & maybe_unit. to_lowercase ( ) ;
87+ maybe_unit = & unit_lower. as_str ( ) ;
88+
89+ if matches ! ( maybe_unit, "y" | "year" | "years" ) {
8490 check_interval_value_and_unit ( & mut has_years, value, maybe_unit, location) ?;
8591 interval. years = value;
8692 position += 1 ;
8793 continue ;
8894 }
8995
90- if matches ! ( maybe_unit, "mon" | "mons" | "months" ) {
96+ if matches ! ( maybe_unit, "m" | " mon" | "mons" | "months" ) {
9197 check_interval_value_and_unit ( & mut has_months, value, maybe_unit, location) ?;
9298 interval. months = value;
9399 position += 1 ;
94100 continue ;
95101 }
96102
97- if matches ! ( maybe_unit, "day" | "days" ) {
103+ if matches ! ( maybe_unit, "d" | " day" | "days" ) {
98104 check_interval_value_and_unit ( & mut has_days, value, maybe_unit, location) ?;
99105 interval. days = value;
100106 position += 1 ;
101107 continue ;
102108 }
103109
110+ if matches ! ( maybe_unit, "h" | "hour" | "hours" ) {
111+ check_interval_value_and_unit ( & mut has_any_time_part, value, maybe_unit, location) ?;
112+ interval. hours = value;
113+ position += 1 ;
114+ continue ;
115+ }
116+
104117 return Err ( Diagnostic :: error ( & format ! (
105118 "Invalid input syntax for interval unit `{maybe_unit}`" ,
106119 ) )
@@ -113,6 +126,14 @@ fn parse_interval_literal(
113126
114127 // Parse Seconds, Minutes or Hours
115128 if token. contains ( ':' ) {
129+ if has_any_time_part {
130+ return Err (
131+ Diagnostic :: error ( "You can't have time value twice in same interval" )
132+ . with_location ( location)
133+ . as_boxed ( ) ,
134+ ) ;
135+ }
136+
116137 let time_parts: Vec < & str > = token. split ( ':' ) . collect ( ) ;
117138 if time_parts. len ( ) != 3 && time_parts. len ( ) != 2 {
118139 return Err ( Diagnostic :: error ( "Invalid input syntax for type interval" )
@@ -208,3 +229,30 @@ fn check_interval_value_and_unit(
208229 . with_location ( location)
209230 . as_boxed ( ) )
210231}
232+
233+ #[ cfg( test) ]
234+ mod tests {
235+ use super :: * ;
236+
237+ #[ test]
238+ fn valid_hours ( ) {
239+ let inputs = [ "1 h" , "1 hour" , "1 hours" , "1:00:00" ] ;
240+ for input in inputs {
241+ let parse_result = parse_interval_literal ( input, SourceLocation :: default ( ) ) ;
242+ assert ! ( parse_result. is_ok( ) ) ;
243+
244+ if let Ok ( interval) = parse_result {
245+ assert_eq ! ( interval. hours, 1 ) ;
246+ }
247+ }
248+ }
249+
250+ #[ test]
251+ fn invalid_time ( ) {
252+ let inputs = [ "1 h 1:00:00" , "1 h 1 h" ] ;
253+ for input in inputs {
254+ let parse_result = parse_interval_literal ( input, SourceLocation :: default ( ) ) ;
255+ assert ! ( parse_result. is_err( ) ) ;
256+ }
257+ }
258+ }
0 commit comments