fix: Support expr instead of string for argument to interval (#517)

* fix: use expr instead of string in interval

* chore: resolve CI

Co-authored-by: togami2864 <yoshiaki.togami@plaid.co.jp>
This commit is contained in:
Yoshi Togami 2022-06-13 21:35:06 +09:00 committed by GitHub
parent 99697d26e1
commit d981f70143
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 52 additions and 15 deletions

View file

@ -10,6 +10,8 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#[cfg(not(feature = "std"))]
use alloc::boxed::Box;
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
use alloc::string::String; use alloc::string::String;
use core::fmt; use core::fmt;
@ -19,6 +21,8 @@ use bigdecimal::BigDecimal;
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use super::Expr;
/// Primitive SQL values such as number and string /// Primitive SQL values such as number and string
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@ -50,7 +54,7 @@ pub enum Value {
/// that the `<leading_field>` units >= the units in `<last_field>`, /// that the `<leading_field>` units >= the units in `<last_field>`,
/// so the user will have to reject intervals like `HOUR TO YEAR`. /// so the user will have to reject intervals like `HOUR TO YEAR`.
Interval { Interval {
value: String, value: Box<Expr>,
leading_field: Option<DateTimeField>, leading_field: Option<DateTimeField>,
leading_precision: Option<u64>, leading_precision: Option<u64>,
last_field: Option<DateTimeField>, last_field: Option<DateTimeField>,
@ -88,10 +92,8 @@ impl fmt::Display for Value {
assert!(last_field.is_none()); assert!(last_field.is_none());
write!( write!(
f, f,
"INTERVAL '{}' SECOND ({}, {})", "INTERVAL {} SECOND ({}, {})",
escape_single_quote_string(value), value, leading_precision, fractional_seconds_precision
leading_precision,
fractional_seconds_precision
) )
} }
Value::Interval { Value::Interval {
@ -101,7 +103,7 @@ impl fmt::Display for Value {
last_field, last_field,
fractional_seconds_precision, fractional_seconds_precision,
} => { } => {
write!(f, "INTERVAL '{}'", escape_single_quote_string(value))?; write!(f, "INTERVAL {}", value)?;
if let Some(leading_field) = leading_field { if let Some(leading_field) = leading_field {
write!(f, " {}", leading_field)?; write!(f, " {}", leading_field)?;
} }

View file

@ -994,6 +994,7 @@ impl<'a> Parser<'a> {
/// 4. `INTERVAL '1:1:1.1' HOUR (5) TO SECOND (5)` /// 4. `INTERVAL '1:1:1.1' HOUR (5) TO SECOND (5)`
/// 5. `INTERVAL '1.1' SECOND (2, 2)` /// 5. `INTERVAL '1.1' SECOND (2, 2)`
/// 6. `INTERVAL '1:1' HOUR (5) TO MINUTE (5)` /// 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. /// Note that we do not currently attempt to parse the quoted value.
pub fn parse_literal_interval(&mut self) -> Result<Expr, ParserError> { pub fn parse_literal_interval(&mut self) -> Result<Expr, ParserError> {
@ -1004,13 +1005,13 @@ impl<'a> Parser<'a> {
// The first token in an interval is a string literal which specifies // The first token in an interval is a string literal which specifies
// the duration of the interval. // 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 // Following the string literal is a qualifier which indicates the units
// of the duration specified in the string literal. // of the duration specified in the string literal.
// //
// Note that PostgreSQL allows omitting the qualifier, so we provide // 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() { let leading_field = match self.peek_token() {
Token::Word(kw) Token::Word(kw)
if [ if [
@ -1071,7 +1072,7 @@ impl<'a> Parser<'a> {
}; };
Ok(Expr::Value(Value::Interval { Ok(Expr::Value(Value::Interval {
value, value: Box::new(value),
leading_field, leading_field,
leading_precision, leading_precision,
last_field, last_field,

View file

@ -2649,7 +2649,7 @@ fn parse_literal_interval() {
let select = verified_only_select(sql); let select = verified_only_select(sql);
assert_eq!( assert_eq!(
&Expr::Value(Value::Interval { &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_field: Some(DateTimeField::Year),
leading_precision: None, leading_precision: None,
last_field: Some(DateTimeField::Month), last_field: Some(DateTimeField::Month),
@ -2662,7 +2662,9 @@ fn parse_literal_interval() {
let select = verified_only_select(sql); let select = verified_only_select(sql);
assert_eq!( assert_eq!(
&Expr::Value(Value::Interval { &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_field: Some(DateTimeField::Minute),
leading_precision: Some(5), leading_precision: Some(5),
last_field: Some(DateTimeField::Second), last_field: Some(DateTimeField::Second),
@ -2675,7 +2677,7 @@ fn parse_literal_interval() {
let select = verified_only_select(sql); let select = verified_only_select(sql);
assert_eq!( assert_eq!(
&Expr::Value(Value::Interval { &Expr::Value(Value::Interval {
value: "1".into(), value: Box::new(Expr::Value(Value::SingleQuotedString(String::from("1")))),
leading_field: Some(DateTimeField::Second), leading_field: Some(DateTimeField::Second),
leading_precision: Some(5), leading_precision: Some(5),
last_field: None, last_field: None,
@ -2688,7 +2690,7 @@ fn parse_literal_interval() {
let select = verified_only_select(sql); let select = verified_only_select(sql);
assert_eq!( assert_eq!(
&Expr::Value(Value::Interval { &Expr::Value(Value::Interval {
value: "10".into(), value: Box::new(Expr::Value(Value::SingleQuotedString(String::from("10")))),
leading_field: Some(DateTimeField::Hour), leading_field: Some(DateTimeField::Hour),
leading_precision: None, leading_precision: None,
last_field: None, last_field: None,
@ -2697,11 +2699,41 @@ fn parse_literal_interval() {
expr_from_projection(only(&select.projection)), 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 sql = "SELECT INTERVAL '10' HOUR (1)";
let select = verified_only_select(sql); let select = verified_only_select(sql);
assert_eq!( assert_eq!(
&Expr::Value(Value::Interval { &Expr::Value(Value::Interval {
value: "10".into(), value: Box::new(Expr::Value(Value::SingleQuotedString(String::from("10")))),
leading_field: Some(DateTimeField::Hour), leading_field: Some(DateTimeField::Hour),
leading_precision: Some(1), leading_precision: Some(1),
last_field: None, last_field: None,
@ -2714,7 +2746,9 @@ fn parse_literal_interval() {
let select = verified_only_select(sql); let select = verified_only_select(sql);
assert_eq!( assert_eq!(
&Expr::Value(Value::Interval { &Expr::Value(Value::Interval {
value: "1 DAY".into(), value: Box::new(Expr::Value(Value::SingleQuotedString(String::from(
"1 DAY"
)))),
leading_field: None, leading_field: None,
leading_precision: None, leading_precision: None,
last_field: None, last_field: None,