Skip to content

Commit d1f67bd

Browse files
authored
Preserve double colon casts (and simplify cast representations) (apache#1221)
1 parent 9db20e2 commit d1f67bd

5 files changed

Lines changed: 79 additions & 88 deletions

File tree

src/ast/mod.rs

Lines changed: 45 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,26 @@ impl fmt::Display for MapAccessKey {
408408
}
409409
}
410410

411+
/// The syntax used for in a cast expression.
412+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
413+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
414+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
415+
pub enum CastKind {
416+
/// The standard SQL cast syntax, e.g. `CAST(<expr> as <datatype>)`
417+
Cast,
418+
/// A cast that returns `NULL` on failure, e.g. `TRY_CAST(<expr> as <datatype>)`.
419+
///
420+
/// See <https://docs.snowflake.com/en/sql-reference/functions/try_cast>.
421+
/// See <https://learn.microsoft.com/en-us/sql/t-sql/functions/try-cast-transact-sql>.
422+
TryCast,
423+
/// A cast that returns `NULL` on failure, bigQuery-specific , e.g. `SAFE_CAST(<expr> as <datatype>)`.
424+
///
425+
/// See <https://cloud.google.com/bigquery/docs/reference/standard-sql/functions-and-operators#safe_casting>.
426+
SafeCast,
427+
/// `<expr> :: <datatype>`
428+
DoubleColon,
429+
}
430+
411431
/// An SQL expression of any type.
412432
///
413433
/// The parser does not distinguish between expressions of different types
@@ -546,25 +566,7 @@ pub enum Expr {
546566
},
547567
/// `CAST` an expression to a different data type e.g. `CAST(foo AS VARCHAR(123))`
548568
Cast {
549-
expr: Box<Expr>,
550-
data_type: DataType,
551-
// Optional CAST(string_expression AS type FORMAT format_string_expression) as used by BigQuery
552-
// https://cloud.google.com/bigquery/docs/reference/standard-sql/format-elements#formatting_syntax
553-
format: Option<CastFormat>,
554-
},
555-
/// `TRY_CAST` an expression to a different data type e.g. `TRY_CAST(foo AS VARCHAR(123))`
556-
// this differs from CAST in the choice of how to implement invalid conversions
557-
TryCast {
558-
expr: Box<Expr>,
559-
data_type: DataType,
560-
// Optional CAST(string_expression AS type FORMAT format_string_expression) as used by BigQuery
561-
// https://cloud.google.com/bigquery/docs/reference/standard-sql/format-elements#formatting_syntax
562-
format: Option<CastFormat>,
563-
},
564-
/// `SAFE_CAST` an expression to a different data type e.g. `SAFE_CAST(foo AS FLOAT64)`
565-
// only available for BigQuery: https://cloud.google.com/bigquery/docs/reference/standard-sql/functions-and-operators#safe_casting
566-
// this works the same as `TRY_CAST`
567-
SafeCast {
569+
kind: CastKind,
568570
expr: Box<Expr>,
569571
data_type: DataType,
570572
// Optional CAST(string_expression AS type FORMAT format_string_expression) as used by BigQuery
@@ -989,38 +991,36 @@ impl fmt::Display for Expr {
989991
write!(f, ")")
990992
}
991993
Expr::Cast {
994+
kind,
992995
expr,
993996
data_type,
994997
format,
995-
} => {
996-
if let Some(format) = format {
997-
write!(f, "CAST({expr} AS {data_type} FORMAT {format})")
998-
} else {
999-
write!(f, "CAST({expr} AS {data_type})")
998+
} => match kind {
999+
CastKind::Cast => {
1000+
if let Some(format) = format {
1001+
write!(f, "CAST({expr} AS {data_type} FORMAT {format})")
1002+
} else {
1003+
write!(f, "CAST({expr} AS {data_type})")
1004+
}
10001005
}
1001-
}
1002-
Expr::TryCast {
1003-
expr,
1004-
data_type,
1005-
format,
1006-
} => {
1007-
if let Some(format) = format {
1008-
write!(f, "TRY_CAST({expr} AS {data_type} FORMAT {format})")
1009-
} else {
1010-
write!(f, "TRY_CAST({expr} AS {data_type})")
1006+
CastKind::TryCast => {
1007+
if let Some(format) = format {
1008+
write!(f, "TRY_CAST({expr} AS {data_type} FORMAT {format})")
1009+
} else {
1010+
write!(f, "TRY_CAST({expr} AS {data_type})")
1011+
}
10111012
}
1012-
}
1013-
Expr::SafeCast {
1014-
expr,
1015-
data_type,
1016-
format,
1017-
} => {
1018-
if let Some(format) = format {
1019-
write!(f, "SAFE_CAST({expr} AS {data_type} FORMAT {format})")
1020-
} else {
1021-
write!(f, "SAFE_CAST({expr} AS {data_type})")
1013+
CastKind::SafeCast => {
1014+
if let Some(format) = format {
1015+
write!(f, "SAFE_CAST({expr} AS {data_type} FORMAT {format})")
1016+
} else {
1017+
write!(f, "SAFE_CAST({expr} AS {data_type})")
1018+
}
10221019
}
1023-
}
1020+
CastKind::DoubleColon => {
1021+
write!(f, "{expr}::{data_type}")
1022+
}
1023+
},
10241024
Expr::Extract { field, expr } => write!(f, "EXTRACT({field} FROM {expr})"),
10251025
Expr::Ceil { expr, field } => {
10261026
if field == &DateTimeField::NoDateTime {

src/parser/mod.rs

Lines changed: 12 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,9 +1004,9 @@ impl<'a> Parser<'a> {
10041004
}
10051005
Keyword::CASE => self.parse_case_expr(),
10061006
Keyword::CONVERT => self.parse_convert_expr(),
1007-
Keyword::CAST => self.parse_cast_expr(),
1008-
Keyword::TRY_CAST => self.parse_try_cast_expr(),
1009-
Keyword::SAFE_CAST => self.parse_safe_cast_expr(),
1007+
Keyword::CAST => self.parse_cast_expr(CastKind::Cast),
1008+
Keyword::TRY_CAST => self.parse_cast_expr(CastKind::TryCast),
1009+
Keyword::SAFE_CAST => self.parse_cast_expr(CastKind::SafeCast),
10101010
Keyword::EXISTS => self.parse_exists_expr(false),
10111011
Keyword::EXTRACT => self.parse_extract_expr(),
10121012
Keyword::CEIL => self.parse_ceil_floor_expr(true),
@@ -1491,44 +1491,15 @@ impl<'a> Parser<'a> {
14911491
}
14921492

14931493
/// Parse a SQL CAST function e.g. `CAST(expr AS FLOAT)`
1494-
pub fn parse_cast_expr(&mut self) -> Result<Expr, ParserError> {
1494+
pub fn parse_cast_expr(&mut self, kind: CastKind) -> Result<Expr, ParserError> {
14951495
self.expect_token(&Token::LParen)?;
14961496
let expr = self.parse_expr()?;
14971497
self.expect_keyword(Keyword::AS)?;
14981498
let data_type = self.parse_data_type()?;
14991499
let format = self.parse_optional_cast_format()?;
15001500
self.expect_token(&Token::RParen)?;
15011501
Ok(Expr::Cast {
1502-
expr: Box::new(expr),
1503-
data_type,
1504-
format,
1505-
})
1506-
}
1507-
1508-
/// Parse a SQL TRY_CAST function e.g. `TRY_CAST(expr AS FLOAT)`
1509-
pub fn parse_try_cast_expr(&mut self) -> Result<Expr, ParserError> {
1510-
self.expect_token(&Token::LParen)?;
1511-
let expr = self.parse_expr()?;
1512-
self.expect_keyword(Keyword::AS)?;
1513-
let data_type = self.parse_data_type()?;
1514-
let format = self.parse_optional_cast_format()?;
1515-
self.expect_token(&Token::RParen)?;
1516-
Ok(Expr::TryCast {
1517-
expr: Box::new(expr),
1518-
data_type,
1519-
format,
1520-
})
1521-
}
1522-
1523-
/// Parse a BigQuery SAFE_CAST function e.g. `SAFE_CAST(expr AS FLOAT64)`
1524-
pub fn parse_safe_cast_expr(&mut self) -> Result<Expr, ParserError> {
1525-
self.expect_token(&Token::LParen)?;
1526-
let expr = self.parse_expr()?;
1527-
self.expect_keyword(Keyword::AS)?;
1528-
let data_type = self.parse_data_type()?;
1529-
let format = self.parse_optional_cast_format()?;
1530-
self.expect_token(&Token::RParen)?;
1531-
Ok(Expr::SafeCast {
1502+
kind,
15321503
expr: Box::new(expr),
15331504
data_type,
15341505
format,
@@ -2528,7 +2499,12 @@ impl<'a> Parser<'a> {
25282499
),
25292500
}
25302501
} else if Token::DoubleColon == tok {
2531-
self.parse_pg_cast(expr)
2502+
Ok(Expr::Cast {
2503+
kind: CastKind::DoubleColon,
2504+
expr: Box::new(expr),
2505+
data_type: self.parse_data_type()?,
2506+
format: None,
2507+
})
25322508
} else if Token::ExclamationMark == tok {
25332509
// PostgreSQL factorial operation
25342510
Ok(Expr::UnaryOp {
@@ -2702,6 +2678,7 @@ impl<'a> Parser<'a> {
27022678
/// Parse a postgresql casting style which is in the form of `expr::datatype`
27032679
pub fn parse_pg_cast(&mut self, expr: Expr) -> Result<Expr, ParserError> {
27042680
Ok(Expr::Cast {
2681+
kind: CastKind::DoubleColon,
27052682
expr: Box::new(expr),
27062683
data_type: self.parse_data_type()?,
27072684
format: None,

tests/sqlparser_common.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2107,6 +2107,7 @@ fn parse_cast() {
21072107
let select = verified_only_select(sql);
21082108
assert_eq!(
21092109
&Expr::Cast {
2110+
kind: CastKind::Cast,
21102111
expr: Box::new(Expr::Identifier(Ident::new("id"))),
21112112
data_type: DataType::BigInt(None),
21122113
format: None,
@@ -2118,6 +2119,7 @@ fn parse_cast() {
21182119
let select = verified_only_select(sql);
21192120
assert_eq!(
21202121
&Expr::Cast {
2122+
kind: CastKind::Cast,
21212123
expr: Box::new(Expr::Identifier(Ident::new("id"))),
21222124
data_type: DataType::TinyInt(None),
21232125
format: None,
@@ -2145,6 +2147,7 @@ fn parse_cast() {
21452147
let select = verified_only_select(sql);
21462148
assert_eq!(
21472149
&Expr::Cast {
2150+
kind: CastKind::Cast,
21482151
expr: Box::new(Expr::Identifier(Ident::new("id"))),
21492152
data_type: DataType::Nvarchar(Some(50)),
21502153
format: None,
@@ -2156,6 +2159,7 @@ fn parse_cast() {
21562159
let select = verified_only_select(sql);
21572160
assert_eq!(
21582161
&Expr::Cast {
2162+
kind: CastKind::Cast,
21592163
expr: Box::new(Expr::Identifier(Ident::new("id"))),
21602164
data_type: DataType::Clob(None),
21612165
format: None,
@@ -2167,6 +2171,7 @@ fn parse_cast() {
21672171
let select = verified_only_select(sql);
21682172
assert_eq!(
21692173
&Expr::Cast {
2174+
kind: CastKind::Cast,
21702175
expr: Box::new(Expr::Identifier(Ident::new("id"))),
21712176
data_type: DataType::Clob(Some(50)),
21722177
format: None,
@@ -2178,6 +2183,7 @@ fn parse_cast() {
21782183
let select = verified_only_select(sql);
21792184
assert_eq!(
21802185
&Expr::Cast {
2186+
kind: CastKind::Cast,
21812187
expr: Box::new(Expr::Identifier(Ident::new("id"))),
21822188
data_type: DataType::Binary(Some(50)),
21832189
format: None,
@@ -2189,6 +2195,7 @@ fn parse_cast() {
21892195
let select = verified_only_select(sql);
21902196
assert_eq!(
21912197
&Expr::Cast {
2198+
kind: CastKind::Cast,
21922199
expr: Box::new(Expr::Identifier(Ident::new("id"))),
21932200
data_type: DataType::Varbinary(Some(50)),
21942201
format: None,
@@ -2200,6 +2207,7 @@ fn parse_cast() {
22002207
let select = verified_only_select(sql);
22012208
assert_eq!(
22022209
&Expr::Cast {
2210+
kind: CastKind::Cast,
22032211
expr: Box::new(Expr::Identifier(Ident::new("id"))),
22042212
data_type: DataType::Blob(None),
22052213
format: None,
@@ -2211,6 +2219,7 @@ fn parse_cast() {
22112219
let select = verified_only_select(sql);
22122220
assert_eq!(
22132221
&Expr::Cast {
2222+
kind: CastKind::Cast,
22142223
expr: Box::new(Expr::Identifier(Ident::new("id"))),
22152224
data_type: DataType::Blob(Some(50)),
22162225
format: None,
@@ -2222,6 +2231,7 @@ fn parse_cast() {
22222231
let select = verified_only_select(sql);
22232232
assert_eq!(
22242233
&Expr::Cast {
2234+
kind: CastKind::Cast,
22252235
expr: Box::new(Expr::Identifier(Ident::new("details"))),
22262236
data_type: DataType::JSONB,
22272237
format: None,
@@ -2235,7 +2245,8 @@ fn parse_try_cast() {
22352245
let sql = "SELECT TRY_CAST(id AS BIGINT) FROM customer";
22362246
let select = verified_only_select(sql);
22372247
assert_eq!(
2238-
&Expr::TryCast {
2248+
&Expr::Cast {
2249+
kind: CastKind::TryCast,
22392250
expr: Box::new(Expr::Identifier(Ident::new("id"))),
22402251
data_type: DataType::BigInt(None),
22412252
format: None,

tests/sqlparser_postgres.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ fn parse_create_table_with_defaults() {
328328
location: None,
329329
..
330330
} => {
331+
use pretty_assertions::assert_eq;
331332
assert_eq!("public.customer", name.to_string());
332333
assert_eq!(
333334
columns,
@@ -422,9 +423,7 @@ fn parse_create_table_with_defaults() {
422423
options: vec![
423424
ColumnOptionDef {
424425
name: None,
425-
option: ColumnOption::Default(
426-
pg().verified_expr("CAST(now() AS TEXT)")
427-
)
426+
option: ColumnOption::Default(pg().verified_expr("now()::TEXT"))
428427
},
429428
ColumnOptionDef {
430429
name: None,
@@ -498,15 +497,15 @@ fn parse_create_table_from_pg_dump() {
498497
active int
499498
)";
500499
pg().one_statement_parses_to(sql, "CREATE TABLE public.customer (\
501-
customer_id INTEGER DEFAULT nextval(CAST('public.customer_customer_id_seq' AS REGCLASS)) NOT NULL, \
500+
customer_id INTEGER DEFAULT nextval('public.customer_customer_id_seq'::REGCLASS) NOT NULL, \
502501
store_id SMALLINT NOT NULL, \
503502
first_name CHARACTER VARYING(45) NOT NULL, \
504503
last_name CHARACTER VARYING(45) NOT NULL, \
505504
info TEXT[], \
506505
address_id SMALLINT NOT NULL, \
507506
activebool BOOLEAN DEFAULT true NOT NULL, \
508-
create_date DATE DEFAULT CAST(now() AS DATE) NOT NULL, \
509-
create_date1 DATE DEFAULT CAST(CAST('now' AS TEXT) AS DATE) NOT NULL, \
507+
create_date DATE DEFAULT now()::DATE NOT NULL, \
508+
create_date1 DATE DEFAULT 'now'::TEXT::DATE NOT NULL, \
510509
last_update TIMESTAMP WITHOUT TIME ZONE DEFAULT now(), \
511510
release_year public.year, \
512511
active INT\
@@ -1448,11 +1447,13 @@ fn parse_execute() {
14481447
parameters: vec![],
14491448
using: vec![
14501449
Expr::Cast {
1450+
kind: CastKind::Cast,
14511451
expr: Box::new(Expr::Value(Value::Number("1337".parse().unwrap(), false))),
14521452
data_type: DataType::SmallInt(None),
14531453
format: None
14541454
},
14551455
Expr::Cast {
1456+
kind: CastKind::Cast,
14561457
expr: Box::new(Expr::Value(Value::Number("7331".parse().unwrap(), false))),
14571458
data_type: DataType::SmallInt(None),
14581459
format: None
@@ -1908,6 +1909,7 @@ fn parse_array_index_expr() {
19081909
assert_eq!(
19091910
&Expr::ArrayIndex {
19101911
obj: Box::new(Expr::Nested(Box::new(Expr::Cast {
1912+
kind: CastKind::Cast,
19111913
expr: Box::new(Expr::Array(Array {
19121914
elem: vec![Expr::Array(Array {
19131915
elem: vec![num[2].clone(), num[3].clone(),],

tests/sqlparser_snowflake.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ fn parse_array() {
168168
let select = snowflake().verified_only_select(sql);
169169
assert_eq!(
170170
&Expr::Cast {
171+
kind: CastKind::Cast,
171172
expr: Box::new(Expr::Identifier(Ident::new("a"))),
172173
data_type: DataType::Array(ArrayElemTypeDef::None),
173174
format: None,
@@ -228,7 +229,7 @@ fn parse_json_using_colon() {
228229
select.projection[0]
229230
);
230231

231-
snowflake().one_statement_parses_to("SELECT a:b::int FROM t", "SELECT CAST(a:b AS INT) FROM t");
232+
snowflake().verified_stmt("SELECT a:b::INT FROM t");
232233

233234
let sql = "SELECT a:start, a:end FROM t";
234235
let select = snowflake().verified_only_select(sql);

0 commit comments

Comments
 (0)