mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-07-15 12:34:59 +00:00
Move Value::Interval
to Expr::Interval
(#609)
* refactor(value): convert Value::Interval to Expr::Interval * test(sqlparser_common): modify test case * refactor(parser): rename func parse_interval * refactor(tests) rename parse_interval test func
This commit is contained in:
parent
d971a029dd
commit
3ac1bb5b80
4 changed files with 81 additions and 83 deletions
|
@ -410,6 +410,25 @@ pub enum Expr {
|
|||
ArrayIndex { obj: Box<Expr>, indexes: Vec<Expr> },
|
||||
/// An array expression e.g. `ARRAY[1, 2]`
|
||||
Array(Array),
|
||||
/// 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 {
|
||||
|
@ -722,6 +741,44 @@ impl fmt::Display for Expr {
|
|||
} => {
|
||||
write!(f, "{} AT TIME ZONE '{}'", timestamp, time_zone)
|
||||
}
|
||||
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(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,8 +21,6 @@ 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))]
|
||||
|
@ -45,25 +43,6 @@ pub enum Value {
|
|||
DoubleQuotedString(String),
|
||||
/// Boolean value true or false
|
||||
Boolean(bool),
|
||||
/// 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>,
|
||||
},
|
||||
/// `NULL` value
|
||||
Null,
|
||||
/// `?` or `$` Prepared statement arg placeholder
|
||||
|
@ -80,44 +59,6 @@ impl fmt::Display for Value {
|
|||
Value::NationalStringLiteral(v) => write!(f, "N'{}'", v),
|
||||
Value::HexStringLiteral(v) => write!(f, "X'{}'", v),
|
||||
Value::Boolean(v) => write!(f, "{}", v),
|
||||
Value::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
|
||||
)
|
||||
}
|
||||
Value::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(())
|
||||
}
|
||||
Value::Null => write!(f, "NULL"),
|
||||
Value::Placeholder(v) => write!(f, "{}", v),
|
||||
}
|
||||
|
|
|
@ -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 => parser.parse_literal_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),
|
||||
|
@ -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 => self.parse_literal_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 => {
|
||||
|
@ -1096,7 +1096,7 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Parse an INTERVAL literal.
|
||||
/// Parse an INTERVAL expression.
|
||||
///
|
||||
/// Some syntactically valid intervals:
|
||||
///
|
||||
|
@ -1109,7 +1109,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_literal_interval(&mut self) -> Result<Expr, 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
|
||||
|
@ -1183,13 +1183,13 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
};
|
||||
|
||||
Ok(Expr::Value(Value::Interval {
|
||||
Ok(Expr::Interval {
|
||||
value: Box::new(value),
|
||||
leading_field,
|
||||
leading_precision,
|
||||
last_field,
|
||||
fractional_seconds_precision: fsec_precision,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse an operator following an expression
|
||||
|
@ -3178,7 +3178,7 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
// Interval types can be followed by a complicated interval
|
||||
// qualifier that we don't currently support. See
|
||||
// parse_interval_literal for a taste.
|
||||
// parse_interval for a taste.
|
||||
Keyword::INTERVAL => Ok(DataType::Interval),
|
||||
Keyword::REGCLASS => Ok(DataType::Regclass),
|
||||
Keyword::STRING => Ok(DataType::String),
|
||||
|
|
|
@ -2930,24 +2930,24 @@ fn parse_literal_timestamp_with_time_zone() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn parse_literal_interval() {
|
||||
fn parse_interval() {
|
||||
let sql = "SELECT INTERVAL '1-1' YEAR TO MONTH";
|
||||
let select = verified_only_select(sql);
|
||||
assert_eq!(
|
||||
&Expr::Value(Value::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::Value(Value::Interval {
|
||||
&Expr::Interval {
|
||||
value: Box::new(Expr::Value(Value::SingleQuotedString(String::from(
|
||||
"01:01.01"
|
||||
)))),
|
||||
|
@ -2955,53 +2955,53 @@ fn parse_literal_interval() {
|
|||
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::Value(Value::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::Value(Value::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::Value(Value::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::Value(Value::Interval {
|
||||
&Expr::Interval {
|
||||
value: Box::new(Expr::BinaryOp {
|
||||
left: Box::new(Expr::Value(number("1"))),
|
||||
op: BinaryOperator::Plus,
|
||||
|
@ -3011,27 +3011,27 @@ fn parse_literal_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::Value(Value::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::Value(Value::Interval {
|
||||
&Expr::Interval {
|
||||
value: Box::new(Expr::Value(Value::SingleQuotedString(String::from(
|
||||
"1 DAY"
|
||||
)))),
|
||||
|
@ -3039,7 +3039,7 @@ fn parse_literal_interval() {
|
|||
leading_precision: None,
|
||||
last_field: None,
|
||||
fractional_seconds_precision: None,
|
||||
}),
|
||||
},
|
||||
expr_from_projection(only(&select.projection)),
|
||||
);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue