diff --git a/src/ast/value.rs b/src/ast/value.rs index fe2870f95..fdcd238cd 100644 --- a/src/ast/value.rs +++ b/src/ast/value.rs @@ -37,7 +37,7 @@ pub enum Value { /// `TIMESTAMP '...'` literals Timestamp(String), /// INTERVAL literals, roughly in the following format: - /// `INTERVAL '' [ () ] + /// `INTERVAL '' [ [ () ] ] /// [ TO [ () ] ]`, /// e.g. `INTERVAL '123:45.67' MINUTE(3) TO SECOND(2)`. /// @@ -46,7 +46,7 @@ pub enum Value { /// so the user will have to reject intervals like `HOUR TO YEAR`. Interval { value: String, - leading_field: DateTimeField, + leading_field: Option, leading_precision: Option, last_field: Option, /// The seconds precision can be specified in SQL source as @@ -72,7 +72,7 @@ impl fmt::Display for Value { Value::Timestamp(v) => write!(f, "TIMESTAMP '{}'", escape_single_quote_string(v)), Value::Interval { value, - leading_field: DateTimeField::Second, + leading_field: Some(DateTimeField::Second), leading_precision: Some(leading_precision), last_field, fractional_seconds_precision: Some(fractional_seconds_precision), @@ -95,12 +95,10 @@ impl fmt::Display for Value { last_field, fractional_seconds_precision, } => { - write!( - f, - "INTERVAL '{}' {}", - escape_single_quote_string(value), - leading_field - )?; + write!(f, "INTERVAL '{}'", escape_single_quote_string(value))?; + if let Some(leading_field) = leading_field { + write!(f, " {}", leading_field)?; + } if let Some(leading_precision) = leading_precision { write!(f, " ({})", leading_precision)?; } diff --git a/src/parser.rs b/src/parser.rs index 9a22f4d19..8e20c8fce 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -523,12 +523,21 @@ impl Parser { // Following the string literal is a qualifier which indicates the units // of the duration specified in the string literal. // - // Note that PostgreSQL allows omitting the qualifier, but we currently - // require at least the leading field, in accordance with the ANSI spec. - let leading_field = self.parse_date_time_field()?; + // Note that PostgreSQL allows omitting the qualifier, so we provide + // this more general implemenation. + let leading_field = match self.peek_token() { + Some(Token::Word(kw)) + if ["YEAR", "MONTH", "DAY", "HOUR", "MINUTE", "SECOND"] + .iter() + .any(|d| kw.keyword == *d) => + { + Some(self.parse_date_time_field()?) + } + _ => None, + }; let (leading_precision, last_field, fsec_precision) = - if leading_field == DateTimeField::Second { + if leading_field == Some(DateTimeField::Second) { // SQL mandates special syntax for `SECOND TO SECOND` literals. // Instead of // `SECOND [()] TO SECOND[()]` diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index a63d3d49f..8ff6e7e9b 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -1459,7 +1459,7 @@ fn parse_literal_interval() { assert_eq!( &Expr::Value(Value::Interval { value: "1-1".into(), - leading_field: DateTimeField::Year, + leading_field: Some(DateTimeField::Year), leading_precision: None, last_field: Some(DateTimeField::Month), fractional_seconds_precision: None, @@ -1472,7 +1472,7 @@ fn parse_literal_interval() { assert_eq!( &Expr::Value(Value::Interval { value: "01:01.01".into(), - leading_field: DateTimeField::Minute, + leading_field: Some(DateTimeField::Minute), leading_precision: Some(5), last_field: Some(DateTimeField::Second), fractional_seconds_precision: Some(5), @@ -1485,7 +1485,7 @@ fn parse_literal_interval() { assert_eq!( &Expr::Value(Value::Interval { value: "1".into(), - leading_field: DateTimeField::Second, + leading_field: Some(DateTimeField::Second), leading_precision: Some(5), last_field: None, fractional_seconds_precision: Some(4), @@ -1498,7 +1498,7 @@ fn parse_literal_interval() { assert_eq!( &Expr::Value(Value::Interval { value: "10".into(), - leading_field: DateTimeField::Hour, + leading_field: Some(DateTimeField::Hour), leading_precision: None, last_field: None, fractional_seconds_precision: None, @@ -1511,7 +1511,7 @@ fn parse_literal_interval() { assert_eq!( &Expr::Value(Value::Interval { value: "10".into(), - leading_field: DateTimeField::Hour, + leading_field: Some(DateTimeField::Hour), leading_precision: Some(1), last_field: None, fractional_seconds_precision: None, @@ -1519,6 +1519,19 @@ fn parse_literal_interval() { expr_from_projection(only(&select.projection)), ); + let sql = "SELECT INTERVAL '1 DAY'"; + let select = verified_only_select(sql); + assert_eq!( + &Expr::Value(Value::Interval { + value: "1 DAY".into(), + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + }), + expr_from_projection(only(&select.projection)), + ); + let result = parse_sql_statements("SELECT INTERVAL '1' SECOND TO SECOND"); assert_eq!( ParserError::ParserError("Expected end of statement, found: SECOND".to_string()), @@ -1544,6 +1557,12 @@ fn parse_literal_interval() { verified_only_select("SELECT INTERVAL '1' HOUR TO MINUTE"); verified_only_select("SELECT INTERVAL '1' HOUR TO SECOND"); verified_only_select("SELECT INTERVAL '1' MINUTE TO SECOND"); + verified_only_select("SELECT INTERVAL '1 YEAR'"); + verified_only_select("SELECT INTERVAL '1 YEAR' AS one_year"); + one_statement_parses_to( + "SELECT INTERVAL '1 YEAR' one_year", + "SELECT INTERVAL '1 YEAR' AS one_year", + ); } #[test]