diff --git a/src/ast/value.rs b/src/ast/value.rs index 9c32f27d5..1260f3f15 100644 --- a/src/ast/value.rs +++ b/src/ast/value.rs @@ -10,6 +10,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#[cfg(not(feature = "std"))] +use alloc::boxed::Box; #[cfg(not(feature = "std"))] use alloc::string::String; use core::fmt; @@ -19,6 +21,8 @@ use bigdecimal::BigDecimal; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +use super::Expr; + /// Primitive SQL values such as number and string #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -50,7 +54,7 @@ pub enum Value { /// that the `` units >= the units in ``, /// so the user will have to reject intervals like `HOUR TO YEAR`. Interval { - value: String, + value: Box, leading_field: Option, leading_precision: Option, last_field: Option, @@ -88,10 +92,8 @@ impl fmt::Display for Value { assert!(last_field.is_none()); write!( f, - "INTERVAL '{}' SECOND ({}, {})", - escape_single_quote_string(value), - leading_precision, - fractional_seconds_precision + "INTERVAL {} SECOND ({}, {})", + value, leading_precision, fractional_seconds_precision ) } Value::Interval { @@ -101,7 +103,7 @@ impl fmt::Display for Value { last_field, fractional_seconds_precision, } => { - write!(f, "INTERVAL '{}'", escape_single_quote_string(value))?; + write!(f, "INTERVAL {}", value)?; if let Some(leading_field) = leading_field { write!(f, " {}", leading_field)?; } diff --git a/src/parser.rs b/src/parser.rs index 36c2075c0..afed121ba 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -991,6 +991,7 @@ impl<'a> Parser<'a> { /// 4. `INTERVAL '1:1:1.1' HOUR (5) TO SECOND (5)` /// 5. `INTERVAL '1.1' SECOND (2, 2)` /// 6. `INTERVAL '1:1' HOUR (5) TO MINUTE (5)` + /// 7. (MySql and BigQuey only):`INTERVAL 1 DAY` /// /// Note that we do not currently attempt to parse the quoted value. pub fn parse_literal_interval(&mut self) -> Result { @@ -1001,13 +1002,13 @@ impl<'a> Parser<'a> { // The first token in an interval is a string literal which specifies // the duration of the interval. - let value = self.parse_literal_string()?; + let value = self.parse_expr()?; // 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, so we provide - // this more general implemenation. + // this more general implementation. let leading_field = match self.peek_token() { Token::Word(kw) if [ @@ -1068,7 +1069,7 @@ impl<'a> Parser<'a> { }; Ok(Expr::Value(Value::Interval { - value, + value: Box::new(value), leading_field, leading_precision, last_field, diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 83dacb046..22006e6a6 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -2634,7 +2634,7 @@ fn parse_literal_interval() { let select = verified_only_select(sql); assert_eq!( &Expr::Value(Value::Interval { - value: "1-1".into(), + value: Box::new(Expr::Value(Value::SingleQuotedString(String::from("1-1")))), leading_field: Some(DateTimeField::Year), leading_precision: None, last_field: Some(DateTimeField::Month), @@ -2647,7 +2647,9 @@ fn parse_literal_interval() { let select = verified_only_select(sql); assert_eq!( &Expr::Value(Value::Interval { - value: "01:01.01".into(), + value: Box::new(Expr::Value(Value::SingleQuotedString(String::from( + "01:01.01" + )))), leading_field: Some(DateTimeField::Minute), leading_precision: Some(5), last_field: Some(DateTimeField::Second), @@ -2660,7 +2662,7 @@ fn parse_literal_interval() { let select = verified_only_select(sql); assert_eq!( &Expr::Value(Value::Interval { - value: "1".into(), + value: Box::new(Expr::Value(Value::SingleQuotedString(String::from("1")))), leading_field: Some(DateTimeField::Second), leading_precision: Some(5), last_field: None, @@ -2673,7 +2675,7 @@ fn parse_literal_interval() { let select = verified_only_select(sql); assert_eq!( &Expr::Value(Value::Interval { - value: "10".into(), + value: Box::new(Expr::Value(Value::SingleQuotedString(String::from("10")))), leading_field: Some(DateTimeField::Hour), leading_precision: None, last_field: None, @@ -2682,11 +2684,41 @@ fn parse_literal_interval() { expr_from_projection(only(&select.projection)), ); + let sql = "SELECT INTERVAL 5 DAY"; + let select = verified_only_select(sql); + assert_eq!( + &Expr::Value(Value::Interval { + value: Box::new(Expr::Value(number("5"))), + leading_field: Some(DateTimeField::Day), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + }), + expr_from_projection(only(&select.projection)), + ); + + let sql = "SELECT INTERVAL 1 + 1 DAY"; + let select = verified_only_select(sql); + assert_eq!( + &Expr::Value(Value::Interval { + value: Box::new(Expr::BinaryOp { + left: Box::new(Expr::Value(number("1"))), + op: BinaryOperator::Plus, + right: Box::new(Expr::Value(number("1"))) + }), + leading_field: Some(DateTimeField::Day), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + }), + expr_from_projection(only(&select.projection)), + ); + let sql = "SELECT INTERVAL '10' HOUR (1)"; let select = verified_only_select(sql); assert_eq!( &Expr::Value(Value::Interval { - value: "10".into(), + value: Box::new(Expr::Value(Value::SingleQuotedString(String::from("10")))), leading_field: Some(DateTimeField::Hour), leading_precision: Some(1), last_field: None, @@ -2699,7 +2731,9 @@ fn parse_literal_interval() { let select = verified_only_select(sql); assert_eq!( &Expr::Value(Value::Interval { - value: "1 DAY".into(), + value: Box::new(Expr::Value(Value::SingleQuotedString(String::from( + "1 DAY" + )))), leading_field: None, leading_precision: None, last_field: None,