Skip to content

Commit e24951e

Browse files
togami2864togami2864
and
togami2864
authored
feat: support SAFE_CAST for bigquery (#552)
Co-authored-by: togami2864 <[email protected]>
1 parent 2a04212 commit e24951e

File tree

4 files changed

+29
-0
lines changed

4 files changed

+29
-0
lines changed

src/ast/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,13 @@ pub enum Expr {
285285
expr: Box<Expr>,
286286
data_type: DataType,
287287
},
288+
/// SAFE_CAST an expression to a different data type e.g. `SAFE_CAST(foo AS FLOAT64)`
289+
// only available for BigQuery: https://cloud.google.com/bigquery/docs/reference/standard-sql/functions-and-operators#safe_casting
290+
// this works the same as `TRY_CAST`
291+
SafeCast {
292+
expr: Box<Expr>,
293+
data_type: DataType,
294+
},
288295
/// AT a timestamp to a different timezone e.g. `FROM_UNIXTIME(0) AT TIME ZONE 'UTC-06:00'`
289296
AtTimeZone {
290297
timestamp: Box<Expr>,
@@ -442,6 +449,7 @@ impl fmt::Display for Expr {
442449
}
443450
Expr::Cast { expr, data_type } => write!(f, "CAST({} AS {})", expr, data_type),
444451
Expr::TryCast { expr, data_type } => write!(f, "TRY_CAST({} AS {})", expr, data_type),
452+
Expr::SafeCast { expr, data_type } => write!(f, "SAFE_CAST({} AS {})", expr, data_type),
445453
Expr::Extract { field, expr } => write!(f, "EXTRACT({} FROM {})", field, expr),
446454
Expr::Position { expr, r#in } => write!(f, "POSITION({} IN {})", expr, r#in),
447455
Expr::Collate { expr, collation } => write!(f, "{} COLLATE {}", expr, collation),

src/keywords.rs

+1
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,7 @@ define_keywords!(
439439
ROWID,
440440
ROWS,
441441
ROW_NUMBER,
442+
SAFE_CAST,
442443
SAVEPOINT,
443444
SCHEMA,
444445
SCOPE,

src/parser.rs

+14
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,7 @@ impl<'a> Parser<'a> {
426426
Keyword::CASE => self.parse_case_expr(),
427427
Keyword::CAST => self.parse_cast_expr(),
428428
Keyword::TRY_CAST => self.parse_try_cast_expr(),
429+
Keyword::SAFE_CAST => self.parse_safe_cast_expr(),
429430
Keyword::EXISTS => self.parse_exists_expr(false),
430431
Keyword::EXTRACT => self.parse_extract_expr(),
431432
Keyword::POSITION => self.parse_position_expr(),
@@ -780,6 +781,19 @@ impl<'a> Parser<'a> {
780781
})
781782
}
782783

784+
/// Parse a BigQuery SAFE_CAST function e.g. `SAFE_CAST(expr AS FLOAT64)`
785+
pub fn parse_safe_cast_expr(&mut self) -> Result<Expr, ParserError> {
786+
self.expect_token(&Token::LParen)?;
787+
let expr = self.parse_expr()?;
788+
self.expect_keyword(Keyword::AS)?;
789+
let data_type = self.parse_data_type()?;
790+
self.expect_token(&Token::RParen)?;
791+
Ok(Expr::SafeCast {
792+
expr: Box::new(expr),
793+
data_type,
794+
})
795+
}
796+
783797
/// Parse a SQL EXISTS expression e.g. `WHERE EXISTS(SELECT ...)`.
784798
pub fn parse_exists_expr(&mut self, negated: bool) -> Result<Expr, ParserError> {
785799
self.expect_token(&Token::LParen)?;

tests/sqlparser_bigquery.rs

+6
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ fn parse_table_identifiers() {
9494
test_table_ident("abc5.GROUP", vec![Ident::new("abc5"), Ident::new("GROUP")]);
9595
}
9696

97+
#[test]
98+
fn parse_cast_type() {
99+
let sql = r#"SELECT SAFE_CAST(1 AS INT64)"#;
100+
bigquery().verified_only_select(sql);
101+
}
102+
97103
fn bigquery() -> TestedDialects {
98104
TestedDialects {
99105
dialects: vec![Box::new(BigQueryDialect {})],

0 commit comments

Comments
 (0)