mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-11-01 23:50:56 +00:00
Preserve double colon casts (and simplify cast representations) (#1221)
This commit is contained in:
parent
9db20e293f
commit
d1f67bdc47
5 changed files with 79 additions and 88 deletions
|
|
@ -408,6 +408,26 @@ impl fmt::Display for MapAccessKey {
|
|||
}
|
||||
}
|
||||
|
||||
/// The syntax used for in a cast expression.
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub enum CastKind {
|
||||
/// The standard SQL cast syntax, e.g. `CAST(<expr> as <datatype>)`
|
||||
Cast,
|
||||
/// A cast that returns `NULL` on failure, e.g. `TRY_CAST(<expr> as <datatype>)`.
|
||||
///
|
||||
/// See <https://docs.snowflake.com/en/sql-reference/functions/try_cast>.
|
||||
/// See <https://learn.microsoft.com/en-us/sql/t-sql/functions/try-cast-transact-sql>.
|
||||
TryCast,
|
||||
/// A cast that returns `NULL` on failure, bigQuery-specific , e.g. `SAFE_CAST(<expr> as <datatype>)`.
|
||||
///
|
||||
/// See <https://cloud.google.com/bigquery/docs/reference/standard-sql/functions-and-operators#safe_casting>.
|
||||
SafeCast,
|
||||
/// `<expr> :: <datatype>`
|
||||
DoubleColon,
|
||||
}
|
||||
|
||||
/// An SQL expression of any type.
|
||||
///
|
||||
/// The parser does not distinguish between expressions of different types
|
||||
|
|
@ -546,25 +566,7 @@ pub enum Expr {
|
|||
},
|
||||
/// `CAST` an expression to a different data type e.g. `CAST(foo AS VARCHAR(123))`
|
||||
Cast {
|
||||
expr: Box<Expr>,
|
||||
data_type: DataType,
|
||||
// Optional CAST(string_expression AS type FORMAT format_string_expression) as used by BigQuery
|
||||
// https://cloud.google.com/bigquery/docs/reference/standard-sql/format-elements#formatting_syntax
|
||||
format: Option<CastFormat>,
|
||||
},
|
||||
/// `TRY_CAST` an expression to a different data type e.g. `TRY_CAST(foo AS VARCHAR(123))`
|
||||
// this differs from CAST in the choice of how to implement invalid conversions
|
||||
TryCast {
|
||||
expr: Box<Expr>,
|
||||
data_type: DataType,
|
||||
// Optional CAST(string_expression AS type FORMAT format_string_expression) as used by BigQuery
|
||||
// https://cloud.google.com/bigquery/docs/reference/standard-sql/format-elements#formatting_syntax
|
||||
format: Option<CastFormat>,
|
||||
},
|
||||
/// `SAFE_CAST` an expression to a different data type e.g. `SAFE_CAST(foo AS FLOAT64)`
|
||||
// only available for BigQuery: https://cloud.google.com/bigquery/docs/reference/standard-sql/functions-and-operators#safe_casting
|
||||
// this works the same as `TRY_CAST`
|
||||
SafeCast {
|
||||
kind: CastKind,
|
||||
expr: Box<Expr>,
|
||||
data_type: DataType,
|
||||
// Optional CAST(string_expression AS type FORMAT format_string_expression) as used by BigQuery
|
||||
|
|
@ -989,38 +991,36 @@ impl fmt::Display for Expr {
|
|||
write!(f, ")")
|
||||
}
|
||||
Expr::Cast {
|
||||
kind,
|
||||
expr,
|
||||
data_type,
|
||||
format,
|
||||
} => {
|
||||
if let Some(format) = format {
|
||||
write!(f, "CAST({expr} AS {data_type} FORMAT {format})")
|
||||
} else {
|
||||
write!(f, "CAST({expr} AS {data_type})")
|
||||
} => match kind {
|
||||
CastKind::Cast => {
|
||||
if let Some(format) = format {
|
||||
write!(f, "CAST({expr} AS {data_type} FORMAT {format})")
|
||||
} else {
|
||||
write!(f, "CAST({expr} AS {data_type})")
|
||||
}
|
||||
}
|
||||
}
|
||||
Expr::TryCast {
|
||||
expr,
|
||||
data_type,
|
||||
format,
|
||||
} => {
|
||||
if let Some(format) = format {
|
||||
write!(f, "TRY_CAST({expr} AS {data_type} FORMAT {format})")
|
||||
} else {
|
||||
write!(f, "TRY_CAST({expr} AS {data_type})")
|
||||
CastKind::TryCast => {
|
||||
if let Some(format) = format {
|
||||
write!(f, "TRY_CAST({expr} AS {data_type} FORMAT {format})")
|
||||
} else {
|
||||
write!(f, "TRY_CAST({expr} AS {data_type})")
|
||||
}
|
||||
}
|
||||
}
|
||||
Expr::SafeCast {
|
||||
expr,
|
||||
data_type,
|
||||
format,
|
||||
} => {
|
||||
if let Some(format) = format {
|
||||
write!(f, "SAFE_CAST({expr} AS {data_type} FORMAT {format})")
|
||||
} else {
|
||||
write!(f, "SAFE_CAST({expr} AS {data_type})")
|
||||
CastKind::SafeCast => {
|
||||
if let Some(format) = format {
|
||||
write!(f, "SAFE_CAST({expr} AS {data_type} FORMAT {format})")
|
||||
} else {
|
||||
write!(f, "SAFE_CAST({expr} AS {data_type})")
|
||||
}
|
||||
}
|
||||
}
|
||||
CastKind::DoubleColon => {
|
||||
write!(f, "{expr}::{data_type}")
|
||||
}
|
||||
},
|
||||
Expr::Extract { field, expr } => write!(f, "EXTRACT({field} FROM {expr})"),
|
||||
Expr::Ceil { expr, field } => {
|
||||
if field == &DateTimeField::NoDateTime {
|
||||
|
|
|
|||
|
|
@ -1004,9 +1004,9 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
Keyword::CASE => self.parse_case_expr(),
|
||||
Keyword::CONVERT => self.parse_convert_expr(),
|
||||
Keyword::CAST => self.parse_cast_expr(),
|
||||
Keyword::TRY_CAST => self.parse_try_cast_expr(),
|
||||
Keyword::SAFE_CAST => self.parse_safe_cast_expr(),
|
||||
Keyword::CAST => self.parse_cast_expr(CastKind::Cast),
|
||||
Keyword::TRY_CAST => self.parse_cast_expr(CastKind::TryCast),
|
||||
Keyword::SAFE_CAST => self.parse_cast_expr(CastKind::SafeCast),
|
||||
Keyword::EXISTS => self.parse_exists_expr(false),
|
||||
Keyword::EXTRACT => self.parse_extract_expr(),
|
||||
Keyword::CEIL => self.parse_ceil_floor_expr(true),
|
||||
|
|
@ -1491,7 +1491,7 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
|
||||
/// Parse a SQL CAST function e.g. `CAST(expr AS FLOAT)`
|
||||
pub fn parse_cast_expr(&mut self) -> Result<Expr, ParserError> {
|
||||
pub fn parse_cast_expr(&mut self, kind: CastKind) -> Result<Expr, ParserError> {
|
||||
self.expect_token(&Token::LParen)?;
|
||||
let expr = self.parse_expr()?;
|
||||
self.expect_keyword(Keyword::AS)?;
|
||||
|
|
@ -1499,36 +1499,7 @@ impl<'a> Parser<'a> {
|
|||
let format = self.parse_optional_cast_format()?;
|
||||
self.expect_token(&Token::RParen)?;
|
||||
Ok(Expr::Cast {
|
||||
expr: Box::new(expr),
|
||||
data_type,
|
||||
format,
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse a SQL TRY_CAST function e.g. `TRY_CAST(expr AS FLOAT)`
|
||||
pub fn parse_try_cast_expr(&mut self) -> Result<Expr, ParserError> {
|
||||
self.expect_token(&Token::LParen)?;
|
||||
let expr = self.parse_expr()?;
|
||||
self.expect_keyword(Keyword::AS)?;
|
||||
let data_type = self.parse_data_type()?;
|
||||
let format = self.parse_optional_cast_format()?;
|
||||
self.expect_token(&Token::RParen)?;
|
||||
Ok(Expr::TryCast {
|
||||
expr: Box::new(expr),
|
||||
data_type,
|
||||
format,
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse a BigQuery SAFE_CAST function e.g. `SAFE_CAST(expr AS FLOAT64)`
|
||||
pub fn parse_safe_cast_expr(&mut self) -> Result<Expr, ParserError> {
|
||||
self.expect_token(&Token::LParen)?;
|
||||
let expr = self.parse_expr()?;
|
||||
self.expect_keyword(Keyword::AS)?;
|
||||
let data_type = self.parse_data_type()?;
|
||||
let format = self.parse_optional_cast_format()?;
|
||||
self.expect_token(&Token::RParen)?;
|
||||
Ok(Expr::SafeCast {
|
||||
kind,
|
||||
expr: Box::new(expr),
|
||||
data_type,
|
||||
format,
|
||||
|
|
@ -2528,7 +2499,12 @@ impl<'a> Parser<'a> {
|
|||
),
|
||||
}
|
||||
} else if Token::DoubleColon == tok {
|
||||
self.parse_pg_cast(expr)
|
||||
Ok(Expr::Cast {
|
||||
kind: CastKind::DoubleColon,
|
||||
expr: Box::new(expr),
|
||||
data_type: self.parse_data_type()?,
|
||||
format: None,
|
||||
})
|
||||
} else if Token::ExclamationMark == tok {
|
||||
// PostgreSQL factorial operation
|
||||
Ok(Expr::UnaryOp {
|
||||
|
|
@ -2702,6 +2678,7 @@ impl<'a> Parser<'a> {
|
|||
/// Parse a postgresql casting style which is in the form of `expr::datatype`
|
||||
pub fn parse_pg_cast(&mut self, expr: Expr) -> Result<Expr, ParserError> {
|
||||
Ok(Expr::Cast {
|
||||
kind: CastKind::DoubleColon,
|
||||
expr: Box::new(expr),
|
||||
data_type: self.parse_data_type()?,
|
||||
format: None,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue