mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-07-16 04:54: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> },
|
ArrayIndex { obj: Box<Expr>, indexes: Vec<Expr> },
|
||||||
/// An array expression e.g. `ARRAY[1, 2]`
|
/// An array expression e.g. `ARRAY[1, 2]`
|
||||||
Array(Array),
|
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 {
|
impl fmt::Display for Expr {
|
||||||
|
@ -722,6 +741,44 @@ impl fmt::Display for Expr {
|
||||||
} => {
|
} => {
|
||||||
write!(f, "{} AT TIME ZONE '{}'", timestamp, time_zone)
|
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")]
|
#[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))]
|
||||||
|
@ -45,25 +43,6 @@ pub enum Value {
|
||||||
DoubleQuotedString(String),
|
DoubleQuotedString(String),
|
||||||
/// Boolean value true or false
|
/// Boolean value true or false
|
||||||
Boolean(bool),
|
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` value
|
||||||
Null,
|
Null,
|
||||||
/// `?` or `$` Prepared statement arg placeholder
|
/// `?` or `$` Prepared statement arg placeholder
|
||||||
|
@ -80,44 +59,6 @@ impl fmt::Display for Value {
|
||||||
Value::NationalStringLiteral(v) => write!(f, "N'{}'", v),
|
Value::NationalStringLiteral(v) => write!(f, "N'{}'", v),
|
||||||
Value::HexStringLiteral(v) => write!(f, "X'{}'", v),
|
Value::HexStringLiteral(v) => write!(f, "X'{}'", v),
|
||||||
Value::Boolean(v) => write!(f, "{}", 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::Null => write!(f, "NULL"),
|
||||||
Value::Placeholder(v) => write!(f, "{}", v),
|
Value::Placeholder(v) => write!(f, "{}", v),
|
||||||
}
|
}
|
||||||
|
|
|
@ -402,7 +402,7 @@ impl<'a> Parser<'a> {
|
||||||
// expression that should parse as the column name "date".
|
// expression that should parse as the column name "date".
|
||||||
return_ok_if_some!(self.maybe_parse(|parser| {
|
return_ok_if_some!(self.maybe_parse(|parser| {
|
||||||
match parser.parse_data_type()? {
|
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,
|
// 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
|
// 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),
|
// 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::SUBSTRING => self.parse_substring_expr(),
|
||||||
Keyword::OVERLAY => self.parse_overlay_expr(),
|
Keyword::OVERLAY => self.parse_overlay_expr(),
|
||||||
Keyword::TRIM => self.parse_trim_expr(),
|
Keyword::TRIM => self.parse_trim_expr(),
|
||||||
Keyword::INTERVAL => self.parse_literal_interval(),
|
Keyword::INTERVAL => self.parse_interval(),
|
||||||
Keyword::LISTAGG => self.parse_listagg_expr(),
|
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
|
// 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 => {
|
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:
|
/// Some syntactically valid intervals:
|
||||||
///
|
///
|
||||||
|
@ -1109,7 +1109,7 @@ impl<'a> Parser<'a> {
|
||||||
/// 7. (MySql and BigQuey only):`INTERVAL 1 DAY`
|
/// 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_interval(&mut self) -> Result<Expr, ParserError> {
|
||||||
// The SQL standard allows an optional sign before the value string, but
|
// The SQL standard allows an optional sign before the value string, but
|
||||||
// it is not clear if any implementations support that syntax, so we
|
// 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
|
// 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),
|
value: Box::new(value),
|
||||||
leading_field,
|
leading_field,
|
||||||
leading_precision,
|
leading_precision,
|
||||||
last_field,
|
last_field,
|
||||||
fractional_seconds_precision: fsec_precision,
|
fractional_seconds_precision: fsec_precision,
|
||||||
}))
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an operator following an expression
|
/// Parse an operator following an expression
|
||||||
|
@ -3178,7 +3178,7 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
// Interval types can be followed by a complicated interval
|
// Interval types can be followed by a complicated interval
|
||||||
// qualifier that we don't currently support. See
|
// 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::INTERVAL => Ok(DataType::Interval),
|
||||||
Keyword::REGCLASS => Ok(DataType::Regclass),
|
Keyword::REGCLASS => Ok(DataType::Regclass),
|
||||||
Keyword::STRING => Ok(DataType::String),
|
Keyword::STRING => Ok(DataType::String),
|
||||||
|
|
|
@ -2930,24 +2930,24 @@ fn parse_literal_timestamp_with_time_zone() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_literal_interval() {
|
fn parse_interval() {
|
||||||
let sql = "SELECT INTERVAL '1-1' YEAR TO MONTH";
|
let sql = "SELECT INTERVAL '1-1' YEAR TO MONTH";
|
||||||
let select = verified_only_select(sql);
|
let select = verified_only_select(sql);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::Value(Value::Interval {
|
&Expr::Interval {
|
||||||
value: Box::new(Expr::Value(Value::SingleQuotedString(String::from("1-1")))),
|
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),
|
||||||
fractional_seconds_precision: None,
|
fractional_seconds_precision: None,
|
||||||
}),
|
},
|
||||||
expr_from_projection(only(&select.projection)),
|
expr_from_projection(only(&select.projection)),
|
||||||
);
|
);
|
||||||
|
|
||||||
let sql = "SELECT INTERVAL '01:01.01' MINUTE (5) TO SECOND (5)";
|
let sql = "SELECT INTERVAL '01:01.01' MINUTE (5) TO SECOND (5)";
|
||||||
let select = verified_only_select(sql);
|
let select = verified_only_select(sql);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::Value(Value::Interval {
|
&Expr::Interval {
|
||||||
value: Box::new(Expr::Value(Value::SingleQuotedString(String::from(
|
value: Box::new(Expr::Value(Value::SingleQuotedString(String::from(
|
||||||
"01:01.01"
|
"01:01.01"
|
||||||
)))),
|
)))),
|
||||||
|
@ -2955,53 +2955,53 @@ fn parse_literal_interval() {
|
||||||
leading_precision: Some(5),
|
leading_precision: Some(5),
|
||||||
last_field: Some(DateTimeField::Second),
|
last_field: Some(DateTimeField::Second),
|
||||||
fractional_seconds_precision: Some(5),
|
fractional_seconds_precision: Some(5),
|
||||||
}),
|
},
|
||||||
expr_from_projection(only(&select.projection)),
|
expr_from_projection(only(&select.projection)),
|
||||||
);
|
);
|
||||||
|
|
||||||
let sql = "SELECT INTERVAL '1' SECOND (5, 4)";
|
let sql = "SELECT INTERVAL '1' SECOND (5, 4)";
|
||||||
let select = verified_only_select(sql);
|
let select = verified_only_select(sql);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::Value(Value::Interval {
|
&Expr::Interval {
|
||||||
value: Box::new(Expr::Value(Value::SingleQuotedString(String::from("1")))),
|
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,
|
||||||
fractional_seconds_precision: Some(4),
|
fractional_seconds_precision: Some(4),
|
||||||
}),
|
},
|
||||||
expr_from_projection(only(&select.projection)),
|
expr_from_projection(only(&select.projection)),
|
||||||
);
|
);
|
||||||
|
|
||||||
let sql = "SELECT INTERVAL '10' HOUR";
|
let sql = "SELECT INTERVAL '10' HOUR";
|
||||||
let select = verified_only_select(sql);
|
let select = verified_only_select(sql);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::Value(Value::Interval {
|
&Expr::Interval {
|
||||||
value: Box::new(Expr::Value(Value::SingleQuotedString(String::from("10")))),
|
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,
|
||||||
fractional_seconds_precision: None,
|
fractional_seconds_precision: None,
|
||||||
}),
|
},
|
||||||
expr_from_projection(only(&select.projection)),
|
expr_from_projection(only(&select.projection)),
|
||||||
);
|
);
|
||||||
|
|
||||||
let sql = "SELECT INTERVAL 5 DAY";
|
let sql = "SELECT INTERVAL 5 DAY";
|
||||||
let select = verified_only_select(sql);
|
let select = verified_only_select(sql);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::Value(Value::Interval {
|
&Expr::Interval {
|
||||||
value: Box::new(Expr::Value(number("5"))),
|
value: Box::new(Expr::Value(number("5"))),
|
||||||
leading_field: Some(DateTimeField::Day),
|
leading_field: Some(DateTimeField::Day),
|
||||||
leading_precision: None,
|
leading_precision: None,
|
||||||
last_field: None,
|
last_field: None,
|
||||||
fractional_seconds_precision: None,
|
fractional_seconds_precision: None,
|
||||||
}),
|
},
|
||||||
expr_from_projection(only(&select.projection)),
|
expr_from_projection(only(&select.projection)),
|
||||||
);
|
);
|
||||||
|
|
||||||
let sql = "SELECT INTERVAL 1 + 1 DAY";
|
let sql = "SELECT INTERVAL 1 + 1 DAY";
|
||||||
let select = verified_only_select(sql);
|
let select = verified_only_select(sql);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::Value(Value::Interval {
|
&Expr::Interval {
|
||||||
value: Box::new(Expr::BinaryOp {
|
value: Box::new(Expr::BinaryOp {
|
||||||
left: Box::new(Expr::Value(number("1"))),
|
left: Box::new(Expr::Value(number("1"))),
|
||||||
op: BinaryOperator::Plus,
|
op: BinaryOperator::Plus,
|
||||||
|
@ -3011,27 +3011,27 @@ fn parse_literal_interval() {
|
||||||
leading_precision: None,
|
leading_precision: None,
|
||||||
last_field: None,
|
last_field: None,
|
||||||
fractional_seconds_precision: None,
|
fractional_seconds_precision: None,
|
||||||
}),
|
},
|
||||||
expr_from_projection(only(&select.projection)),
|
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::Interval {
|
||||||
value: Box::new(Expr::Value(Value::SingleQuotedString(String::from("10")))),
|
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,
|
||||||
fractional_seconds_precision: None,
|
fractional_seconds_precision: None,
|
||||||
}),
|
},
|
||||||
expr_from_projection(only(&select.projection)),
|
expr_from_projection(only(&select.projection)),
|
||||||
);
|
);
|
||||||
|
|
||||||
let sql = "SELECT INTERVAL '1 DAY'";
|
let sql = "SELECT INTERVAL '1 DAY'";
|
||||||
let select = verified_only_select(sql);
|
let select = verified_only_select(sql);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::Value(Value::Interval {
|
&Expr::Interval {
|
||||||
value: Box::new(Expr::Value(Value::SingleQuotedString(String::from(
|
value: Box::new(Expr::Value(Value::SingleQuotedString(String::from(
|
||||||
"1 DAY"
|
"1 DAY"
|
||||||
)))),
|
)))),
|
||||||
|
@ -3039,7 +3039,7 @@ fn parse_literal_interval() {
|
||||||
leading_precision: None,
|
leading_precision: None,
|
||||||
last_field: None,
|
last_field: None,
|
||||||
fractional_seconds_precision: None,
|
fractional_seconds_precision: None,
|
||||||
}),
|
},
|
||||||
expr_from_projection(only(&select.projection)),
|
expr_from_projection(only(&select.projection)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue