mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-07-07 17:04:59 +00:00
MsSQL TRY_CONVERT (#1477)
This commit is contained in:
parent
3421e1e4d4
commit
45c5d69b22
8 changed files with 40 additions and 5 deletions
|
@ -669,6 +669,9 @@ pub enum Expr {
|
|||
},
|
||||
/// CONVERT a value to a different data type or character encoding. e.g. `CONVERT(foo USING utf8mb4)`
|
||||
Convert {
|
||||
/// CONVERT (false) or TRY_CONVERT (true)
|
||||
/// <https://learn.microsoft.com/en-us/sql/t-sql/functions/try-convert-transact-sql?view=sql-server-ver16>
|
||||
is_try: bool,
|
||||
/// The expression to convert
|
||||
expr: Box<Expr>,
|
||||
/// The target data type
|
||||
|
@ -1371,13 +1374,14 @@ impl fmt::Display for Expr {
|
|||
}
|
||||
}
|
||||
Expr::Convert {
|
||||
is_try,
|
||||
expr,
|
||||
target_before_value,
|
||||
data_type,
|
||||
charset,
|
||||
styles,
|
||||
} => {
|
||||
write!(f, "CONVERT(")?;
|
||||
write!(f, "{}CONVERT(", if *is_try { "TRY_" } else { "" })?;
|
||||
if let Some(data_type) = data_type {
|
||||
if let Some(charset) = charset {
|
||||
write!(f, "{expr}, {data_type} CHARACTER SET {charset}")
|
||||
|
|
|
@ -107,4 +107,8 @@ impl Dialect for GenericDialect {
|
|||
fn supports_asc_desc_in_column_definition(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn supports_try_convert(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -585,6 +585,11 @@ pub trait Dialect: Debug + Any {
|
|||
fn supports_eq_alias_assigment(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns true if this dialect supports the `TRY_CONVERT` function
|
||||
fn supports_try_convert(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// This represents the operators for which precedence must be defined
|
||||
|
|
|
@ -53,4 +53,8 @@ impl Dialect for MsSqlDialect {
|
|||
fn supports_eq_alias_assigment(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn supports_try_convert(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -771,6 +771,7 @@ define_keywords!(
|
|||
TRUE,
|
||||
TRUNCATE,
|
||||
TRY_CAST,
|
||||
TRY_CONVERT,
|
||||
TUPLE,
|
||||
TYPE,
|
||||
UESCAPE,
|
||||
|
|
|
@ -1023,7 +1023,8 @@ impl<'a> Parser<'a> {
|
|||
self.parse_time_functions(ObjectName(vec![w.to_ident()]))
|
||||
}
|
||||
Keyword::CASE => self.parse_case_expr(),
|
||||
Keyword::CONVERT => self.parse_convert_expr(),
|
||||
Keyword::CONVERT => self.parse_convert_expr(false),
|
||||
Keyword::TRY_CONVERT if self.dialect.supports_try_convert() => self.parse_convert_expr(true),
|
||||
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),
|
||||
|
@ -1614,7 +1615,7 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
|
||||
/// mssql-like convert function
|
||||
fn parse_mssql_convert(&mut self) -> Result<Expr, ParserError> {
|
||||
fn parse_mssql_convert(&mut self, is_try: bool) -> Result<Expr, ParserError> {
|
||||
self.expect_token(&Token::LParen)?;
|
||||
let data_type = self.parse_data_type()?;
|
||||
self.expect_token(&Token::Comma)?;
|
||||
|
@ -1626,6 +1627,7 @@ impl<'a> Parser<'a> {
|
|||
};
|
||||
self.expect_token(&Token::RParen)?;
|
||||
Ok(Expr::Convert {
|
||||
is_try,
|
||||
expr: Box::new(expr),
|
||||
data_type: Some(data_type),
|
||||
charset: None,
|
||||
|
@ -1638,9 +1640,9 @@ impl<'a> Parser<'a> {
|
|||
/// - `CONVERT('héhé' USING utf8mb4)` (MySQL)
|
||||
/// - `CONVERT('héhé', CHAR CHARACTER SET utf8mb4)` (MySQL)
|
||||
/// - `CONVERT(DECIMAL(10, 5), 42)` (MSSQL) - the type comes first
|
||||
pub fn parse_convert_expr(&mut self) -> Result<Expr, ParserError> {
|
||||
pub fn parse_convert_expr(&mut self, is_try: bool) -> Result<Expr, ParserError> {
|
||||
if self.dialect.convert_type_before_value() {
|
||||
return self.parse_mssql_convert();
|
||||
return self.parse_mssql_convert(is_try);
|
||||
}
|
||||
self.expect_token(&Token::LParen)?;
|
||||
let expr = self.parse_expr()?;
|
||||
|
@ -1648,6 +1650,7 @@ impl<'a> Parser<'a> {
|
|||
let charset = self.parse_object_name(false)?;
|
||||
self.expect_token(&Token::RParen)?;
|
||||
return Ok(Expr::Convert {
|
||||
is_try,
|
||||
expr: Box::new(expr),
|
||||
data_type: None,
|
||||
charset: Some(charset),
|
||||
|
@ -1664,6 +1667,7 @@ impl<'a> Parser<'a> {
|
|||
};
|
||||
self.expect_token(&Token::RParen)?;
|
||||
Ok(Expr::Convert {
|
||||
is_try,
|
||||
expr: Box::new(expr),
|
||||
data_type: Some(data_type),
|
||||
charset,
|
||||
|
|
|
@ -11449,3 +11449,14 @@ fn test_alias_equal_expr() {
|
|||
let expected = r#"SELECT x = (a * b) FROM some_table"#;
|
||||
let _ = dialects.one_statement_parses_to(sql, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_convert() {
|
||||
let dialects =
|
||||
all_dialects_where(|d| d.supports_try_convert() && d.convert_type_before_value());
|
||||
dialects.verified_expr("TRY_CONVERT(VARCHAR(MAX), 'foo')");
|
||||
|
||||
let dialects =
|
||||
all_dialects_where(|d| d.supports_try_convert() && !d.convert_type_before_value());
|
||||
dialects.verified_expr("TRY_CONVERT('foo', VARCHAR(MAX))");
|
||||
}
|
||||
|
|
|
@ -464,6 +464,7 @@ fn parse_cast_varchar_max() {
|
|||
fn parse_convert() {
|
||||
let sql = "CONVERT(INT, 1, 2, 3, NULL)";
|
||||
let Expr::Convert {
|
||||
is_try,
|
||||
expr,
|
||||
data_type,
|
||||
charset,
|
||||
|
@ -473,6 +474,7 @@ fn parse_convert() {
|
|||
else {
|
||||
unreachable!()
|
||||
};
|
||||
assert!(!is_try);
|
||||
assert_eq!(Expr::Value(number("1")), *expr);
|
||||
assert_eq!(Some(DataType::Int(None)), data_type);
|
||||
assert!(charset.is_none());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue