Skip to content

Commit

Permalink
Remove the unnecessary ancillary struct Interval
Browse files Browse the repository at this point in the history
  • Loading branch information
ozankabak committed Oct 5, 2022
1 parent cd7a552 commit 10dcfb0
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 93 deletions.
2 changes: 1 addition & 1 deletion sqlparser_bench/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ sqlparser = { path = "../" }
criterion = "0.4"

[[bench]]
harness = false
name = "sqlparser_bench"
harness = false
129 changes: 57 additions & 72 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,73 +205,6 @@ impl fmt::Display for JsonOperator {
}
}

/// INTERVAL literals, roughly in the following format:
/// `INTERVAL '<value>' [ <leading_field> [ (<leading_precision>) ] ]
/// [ TO <last_field> [ (<fractional_seconds_precision>) ] ]`,
/// e.g. `INTERVAL '123:45.67' MINUTE(3) TO SECOND(2)`.
///
/// The parser does not validate the `<value>`, nor does it ensure
/// that the `<leading_field>` units >= the units in `<last_field>`,
/// so the user will have to reject intervals like `HOUR TO YEAR`.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Interval {
pub value: Box<Expr>,
pub leading_field: Option<DateTimeField>,
pub leading_precision: Option<u64>,
pub last_field: Option<DateTimeField>,
/// The seconds precision can be specified in SQL source as
/// `INTERVAL '__' SECOND(_, x)` (in which case the `leading_field`
/// will be `Second` and the `last_field` will be `None`),
/// or as `__ TO SECOND(x)`.
pub fractional_seconds_precision: Option<u64>,
}

impl fmt::Display for Interval {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Interval {
value,
leading_field: Some(DateTimeField::Second),
leading_precision: Some(leading_precision),
last_field,
fractional_seconds_precision: Some(fractional_seconds_precision),
} => {
// When the leading field is SECOND, the parser guarantees that
// the last field is None.
assert!(last_field.is_none());
write!(
f,
"INTERVAL {} SECOND ({}, {})",
value, leading_precision, fractional_seconds_precision
)
}
Interval {
value,
leading_field,
leading_precision,
last_field,
fractional_seconds_precision,
} => {
write!(f, "INTERVAL {}", value)?;
if let Some(leading_field) = leading_field {
write!(f, " {}", leading_field)?;
}
if let Some(leading_precision) = leading_precision {
write!(f, " ({})", leading_precision)?;
}
if let Some(last_field) = last_field {
write!(f, " TO {}", last_field)?;
}
if let Some(fractional_seconds_precision) = fractional_seconds_precision {
write!(f, " ({})", fractional_seconds_precision)?;
}
Ok(())
}
}
}
}

/// An SQL expression of any type.
///
/// The parser does not distinguish between expressions of different types
Expand Down Expand Up @@ -477,8 +410,25 @@ pub enum Expr {
ArrayIndex { obj: Box<Expr>, indexes: Vec<Expr> },
/// An array expression e.g. `ARRAY[1, 2]`
Array(Array),
/// INTERVAL literals, such as `INTERVAL '1 DAY'`
Interval(Interval),
/// INTERVAL literals, roughly in the following format:
/// `INTERVAL '<value>' [ <leading_field> [ (<leading_precision>) ] ]
/// [ TO <last_field> [ (<fractional_seconds_precision>) ] ]`,
/// e.g. `INTERVAL '123:45.67' MINUTE(3) TO SECOND(2)`.
///
/// The parser does not validate the `<value>`, nor does it ensure
/// that the `<leading_field>` units >= the units in `<last_field>`,
/// so the user will have to reject intervals like `HOUR TO YEAR`.
Interval {
value: Box<Expr>,
leading_field: Option<DateTimeField>,
leading_precision: Option<u64>,
last_field: Option<DateTimeField>,
/// The seconds precision can be specified in SQL source as
/// `INTERVAL '__' SECOND(_, x)` (in which case the `leading_field`
/// will be `Second` and the `last_field` will be `None`),
/// or as `__ TO SECOND(x)`.
fractional_seconds_precision: Option<u64>,
},
}

impl fmt::Display for Expr {
Expand Down Expand Up @@ -791,8 +741,43 @@ impl fmt::Display for Expr {
} => {
write!(f, "{} AT TIME ZONE '{}'", timestamp, time_zone)
}
Expr::Interval(interval) => {
write!(f, "{}", interval)
Expr::Interval {
value,
leading_field: Some(DateTimeField::Second),
leading_precision: Some(leading_precision),
last_field,
fractional_seconds_precision: Some(fractional_seconds_precision),
} => {
// When the leading field is SECOND, the parser guarantees that
// the last field is None.
assert!(last_field.is_none());
write!(
f,
"INTERVAL {} SECOND ({}, {})",
value, leading_precision, fractional_seconds_precision
)
}
Expr::Interval {
value,
leading_field,
leading_precision,
last_field,
fractional_seconds_precision,
} => {
write!(f, "INTERVAL {}", value)?;
if let Some(leading_field) = leading_field {
write!(f, " {}", leading_field)?;
}
if let Some(leading_precision) = leading_precision {
write!(f, " ({})", leading_precision)?;
}
if let Some(last_field) = last_field {
write!(f, " TO {}", last_field)?;
}
if let Some(fractional_seconds_precision) = fractional_seconds_precision {
write!(f, " ({})", fractional_seconds_precision)?;
}
Ok(())
}
}
}
Expand Down Expand Up @@ -918,7 +903,7 @@ pub enum RangeBounds {
/// Number literal, e.g `1`, `1.1`
Number(String),
/// Interval, such as `INTERVAL '1 DAY' `
Interval(Interval),
Interval(Expr),
}

impl fmt::Display for RangeBounds {
Expand Down
8 changes: 4 additions & 4 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ impl<'a> Parser<'a> {
// expression that should parse as the column name "date".
return_ok_if_some!(self.maybe_parse(|parser| {
match parser.parse_data_type()? {
DataType::Interval => Ok(Expr::Interval(parser.parse_interval()?)),
DataType::Interval => parser.parse_interval(),
// PostgreSQL allows almost any identifier to be used as custom data type name,
// and we support that in `parse_data_type()`. But unlike Postgres we don't
// have a list of globally reserved keywords (since they vary across dialects),
Expand Down Expand Up @@ -455,7 +455,7 @@ impl<'a> Parser<'a> {
Keyword::SUBSTRING => self.parse_substring_expr(),
Keyword::OVERLAY => self.parse_overlay_expr(),
Keyword::TRIM => self.parse_trim_expr(),
Keyword::INTERVAL => Ok(Expr::Interval(self.parse_interval()?)),
Keyword::INTERVAL => self.parse_interval(),
Keyword::LISTAGG => self.parse_listagg_expr(),
// Treat ARRAY[1,2,3] as an array [1,2,3], otherwise try as subquery or a function call
Keyword::ARRAY if self.peek_token() == Token::LBracket => {
Expand Down Expand Up @@ -1110,7 +1110,7 @@ impl<'a> Parser<'a> {
/// 7. (MySql and BigQuey only):`INTERVAL 1 DAY`
///
/// Note that we do not currently attempt to parse the quoted value.
pub fn parse_interval(&mut self) -> Result<Interval, ParserError> {
pub fn parse_interval(&mut self) -> Result<Expr, ParserError> {
// The SQL standard allows an optional sign before the value string, but
// it is not clear if any implementations support that syntax, so we
// don't currently try to parse it. (The sign can instead be included
Expand Down Expand Up @@ -1185,7 +1185,7 @@ impl<'a> Parser<'a> {
}
};

Ok(Interval {
Ok(Expr::Interval {
value: Box::new(value),
leading_field,
leading_precision,
Expand Down
32 changes: 16 additions & 16 deletions tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2986,74 +2986,74 @@ fn parse_interval() {
let sql = "SELECT INTERVAL '1-1' YEAR TO MONTH";
let select = verified_only_select(sql);
assert_eq!(
&Expr::Interval(Interval {
&Expr::Interval {
value: Box::new(Expr::Value(Value::SingleQuotedString(String::from("1-1")))),
leading_field: Some(DateTimeField::Year),
leading_precision: None,
last_field: Some(DateTimeField::Month),
fractional_seconds_precision: None,
}),
},
expr_from_projection(only(&select.projection)),
);

let sql = "SELECT INTERVAL '01:01.01' MINUTE (5) TO SECOND (5)";
let select = verified_only_select(sql);
assert_eq!(
&Expr::Interval(Interval {
&Expr::Interval {
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),
fractional_seconds_precision: Some(5),
}),
},
expr_from_projection(only(&select.projection)),
);

let sql = "SELECT INTERVAL '1' SECOND (5, 4)";
let select = verified_only_select(sql);
assert_eq!(
&Expr::Interval(Interval {
&Expr::Interval {
value: Box::new(Expr::Value(Value::SingleQuotedString(String::from("1")))),
leading_field: Some(DateTimeField::Second),
leading_precision: Some(5),
last_field: None,
fractional_seconds_precision: Some(4),
}),
},
expr_from_projection(only(&select.projection)),
);

let sql = "SELECT INTERVAL '10' HOUR";
let select = verified_only_select(sql);
assert_eq!(
&Expr::Interval(Interval {
&Expr::Interval {
value: Box::new(Expr::Value(Value::SingleQuotedString(String::from("10")))),
leading_field: Some(DateTimeField::Hour),
leading_precision: None,
last_field: None,
fractional_seconds_precision: None,
}),
},
expr_from_projection(only(&select.projection)),
);

let sql = "SELECT INTERVAL 5 DAY";
let select = verified_only_select(sql);
assert_eq!(
&Expr::Interval(Interval {
&Expr::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::Interval(Interval {
&Expr::Interval {
value: Box::new(Expr::BinaryOp {
left: Box::new(Expr::Value(number("1"))),
op: BinaryOperator::Plus,
Expand All @@ -3063,35 +3063,35 @@ fn parse_interval() {
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::Interval(Interval {
&Expr::Interval {
value: Box::new(Expr::Value(Value::SingleQuotedString(String::from("10")))),
leading_field: Some(DateTimeField::Hour),
leading_precision: Some(1),
last_field: None,
fractional_seconds_precision: None,
}),
},
expr_from_projection(only(&select.projection)),
);

let sql = "SELECT INTERVAL '1 DAY'";
let select = verified_only_select(sql);
assert_eq!(
&Expr::Interval(Interval {
&Expr::Interval {
value: Box::new(Expr::Value(Value::SingleQuotedString(String::from(
"1 DAY"
)))),
leading_field: None,
leading_precision: None,
last_field: None,
fractional_seconds_precision: None,
}),
},
expr_from_projection(only(&select.projection)),
);

Expand Down

0 comments on commit 10dcfb0

Please sign in to comment.