Skip to content

Commit d981f70

Browse files
togami2864togami2864
and
togami2864
authored
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 <[email protected]>
1 parent 99697d2 commit d981f70

File tree

3 files changed

+52
-15
lines changed

3 files changed

+52
-15
lines changed

src/ast/value.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
// See the License for the specific language governing permissions and
1111
// limitations under the License.
1212

13+
#[cfg(not(feature = "std"))]
14+
use alloc::boxed::Box;
1315
#[cfg(not(feature = "std"))]
1416
use alloc::string::String;
1517
use core::fmt;
@@ -19,6 +21,8 @@ use bigdecimal::BigDecimal;
1921
#[cfg(feature = "serde")]
2022
use serde::{Deserialize, Serialize};
2123

24+
use super::Expr;
25+
2226
/// Primitive SQL values such as number and string
2327
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2428
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -50,7 +54,7 @@ pub enum Value {
5054
/// that the `<leading_field>` units >= the units in `<last_field>`,
5155
/// so the user will have to reject intervals like `HOUR TO YEAR`.
5256
Interval {
53-
value: String,
57+
value: Box<Expr>,
5458
leading_field: Option<DateTimeField>,
5559
leading_precision: Option<u64>,
5660
last_field: Option<DateTimeField>,
@@ -88,10 +92,8 @@ impl fmt::Display for Value {
8892
assert!(last_field.is_none());
8993
write!(
9094
f,
91-
"INTERVAL '{}' SECOND ({}, {})",
92-
escape_single_quote_string(value),
93-
leading_precision,
94-
fractional_seconds_precision
95+
"INTERVAL {} SECOND ({}, {})",
96+
value, leading_precision, fractional_seconds_precision
9597
)
9698
}
9799
Value::Interval {
@@ -101,7 +103,7 @@ impl fmt::Display for Value {
101103
last_field,
102104
fractional_seconds_precision,
103105
} => {
104-
write!(f, "INTERVAL '{}'", escape_single_quote_string(value))?;
106+
write!(f, "INTERVAL {}", value)?;
105107
if let Some(leading_field) = leading_field {
106108
write!(f, " {}", leading_field)?;
107109
}

src/parser.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -994,6 +994,7 @@ impl<'a> Parser<'a> {
994994
/// 4. `INTERVAL '1:1:1.1' HOUR (5) TO SECOND (5)`
995995
/// 5. `INTERVAL '1.1' SECOND (2, 2)`
996996
/// 6. `INTERVAL '1:1' HOUR (5) TO MINUTE (5)`
997+
/// 7. (MySql and BigQuey only):`INTERVAL 1 DAY`
997998
///
998999
/// Note that we do not currently attempt to parse the quoted value.
9991000
pub fn parse_literal_interval(&mut self) -> Result<Expr, ParserError> {
@@ -1004,13 +1005,13 @@ impl<'a> Parser<'a> {
10041005

10051006
// The first token in an interval is a string literal which specifies
10061007
// the duration of the interval.
1007-
let value = self.parse_literal_string()?;
1008+
let value = self.parse_expr()?;
10081009

10091010
// Following the string literal is a qualifier which indicates the units
10101011
// of the duration specified in the string literal.
10111012
//
10121013
// Note that PostgreSQL allows omitting the qualifier, so we provide
1013-
// this more general implemenation.
1014+
// this more general implementation.
10141015
let leading_field = match self.peek_token() {
10151016
Token::Word(kw)
10161017
if [
@@ -1071,7 +1072,7 @@ impl<'a> Parser<'a> {
10711072
};
10721073

10731074
Ok(Expr::Value(Value::Interval {
1074-
value,
1075+
value: Box::new(value),
10751076
leading_field,
10761077
leading_precision,
10771078
last_field,

tests/sqlparser_common.rs

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2649,7 +2649,7 @@ fn parse_literal_interval() {
26492649
let select = verified_only_select(sql);
26502650
assert_eq!(
26512651
&Expr::Value(Value::Interval {
2652-
value: "1-1".into(),
2652+
value: Box::new(Expr::Value(Value::SingleQuotedString(String::from("1-1")))),
26532653
leading_field: Some(DateTimeField::Year),
26542654
leading_precision: None,
26552655
last_field: Some(DateTimeField::Month),
@@ -2662,7 +2662,9 @@ fn parse_literal_interval() {
26622662
let select = verified_only_select(sql);
26632663
assert_eq!(
26642664
&Expr::Value(Value::Interval {
2665-
value: "01:01.01".into(),
2665+
value: Box::new(Expr::Value(Value::SingleQuotedString(String::from(
2666+
"01:01.01"
2667+
)))),
26662668
leading_field: Some(DateTimeField::Minute),
26672669
leading_precision: Some(5),
26682670
last_field: Some(DateTimeField::Second),
@@ -2675,7 +2677,7 @@ fn parse_literal_interval() {
26752677
let select = verified_only_select(sql);
26762678
assert_eq!(
26772679
&Expr::Value(Value::Interval {
2678-
value: "1".into(),
2680+
value: Box::new(Expr::Value(Value::SingleQuotedString(String::from("1")))),
26792681
leading_field: Some(DateTimeField::Second),
26802682
leading_precision: Some(5),
26812683
last_field: None,
@@ -2688,7 +2690,7 @@ fn parse_literal_interval() {
26882690
let select = verified_only_select(sql);
26892691
assert_eq!(
26902692
&Expr::Value(Value::Interval {
2691-
value: "10".into(),
2693+
value: Box::new(Expr::Value(Value::SingleQuotedString(String::from("10")))),
26922694
leading_field: Some(DateTimeField::Hour),
26932695
leading_precision: None,
26942696
last_field: None,
@@ -2697,11 +2699,41 @@ fn parse_literal_interval() {
26972699
expr_from_projection(only(&select.projection)),
26982700
);
26992701

2702+
let sql = "SELECT INTERVAL 5 DAY";
2703+
let select = verified_only_select(sql);
2704+
assert_eq!(
2705+
&Expr::Value(Value::Interval {
2706+
value: Box::new(Expr::Value(number("5"))),
2707+
leading_field: Some(DateTimeField::Day),
2708+
leading_precision: None,
2709+
last_field: None,
2710+
fractional_seconds_precision: None,
2711+
}),
2712+
expr_from_projection(only(&select.projection)),
2713+
);
2714+
2715+
let sql = "SELECT INTERVAL 1 + 1 DAY";
2716+
let select = verified_only_select(sql);
2717+
assert_eq!(
2718+
&Expr::Value(Value::Interval {
2719+
value: Box::new(Expr::BinaryOp {
2720+
left: Box::new(Expr::Value(number("1"))),
2721+
op: BinaryOperator::Plus,
2722+
right: Box::new(Expr::Value(number("1")))
2723+
}),
2724+
leading_field: Some(DateTimeField::Day),
2725+
leading_precision: None,
2726+
last_field: None,
2727+
fractional_seconds_precision: None,
2728+
}),
2729+
expr_from_projection(only(&select.projection)),
2730+
);
2731+
27002732
let sql = "SELECT INTERVAL '10' HOUR (1)";
27012733
let select = verified_only_select(sql);
27022734
assert_eq!(
27032735
&Expr::Value(Value::Interval {
2704-
value: "10".into(),
2736+
value: Box::new(Expr::Value(Value::SingleQuotedString(String::from("10")))),
27052737
leading_field: Some(DateTimeField::Hour),
27062738
leading_precision: Some(1),
27072739
last_field: None,
@@ -2714,7 +2746,9 @@ fn parse_literal_interval() {
27142746
let select = verified_only_select(sql);
27152747
assert_eq!(
27162748
&Expr::Value(Value::Interval {
2717-
value: "1 DAY".into(),
2749+
value: Box::new(Expr::Value(Value::SingleQuotedString(String::from(
2750+
"1 DAY"
2751+
)))),
27182752
leading_field: None,
27192753
leading_precision: None,
27202754
last_field: None,

0 commit comments

Comments
 (0)