Skip to content

Commit 0d42a8a

Browse files
committed
bigquery: CAST AS x FORMAT support
1 parent 7723ea5 commit 0d42a8a

File tree

6 files changed

+109
-4
lines changed

6 files changed

+109
-4
lines changed

src/ast/mod.rs

+36-3
Original file line numberDiff line numberDiff line change
@@ -437,19 +437,22 @@ pub enum Expr {
437437
Cast {
438438
expr: Box<Expr>,
439439
data_type: DataType,
440+
format: Option<Value>,
440441
},
441442
/// TRY_CAST an expression to a different data type e.g. `TRY_CAST(foo AS VARCHAR(123))`
442443
// this differs from CAST in the choice of how to implement invalid conversions
443444
TryCast {
444445
expr: Box<Expr>,
445446
data_type: DataType,
447+
format: Option<Value>,
446448
},
447449
/// SAFE_CAST an expression to a different data type e.g. `SAFE_CAST(foo AS FLOAT64)`
448450
// only available for BigQuery: https://cloud.google.com/bigquery/docs/reference/standard-sql/functions-and-operators#safe_casting
449451
// this works the same as `TRY_CAST`
450452
SafeCast {
451453
expr: Box<Expr>,
452454
data_type: DataType,
455+
format: Option<Value>,
453456
},
454457
/// AT a timestamp to a different timezone e.g. `FROM_UNIXTIME(0) AT TIME ZONE 'UTC-06:00'`
455458
AtTimeZone {
@@ -751,9 +754,39 @@ impl fmt::Display for Expr {
751754
write!(f, "{op}{expr}")
752755
}
753756
}
754-
Expr::Cast { expr, data_type } => write!(f, "CAST({expr} AS {data_type})"),
755-
Expr::TryCast { expr, data_type } => write!(f, "TRY_CAST({expr} AS {data_type})"),
756-
Expr::SafeCast { expr, data_type } => write!(f, "SAFE_CAST({expr} AS {data_type})"),
757+
Expr::Cast {
758+
expr,
759+
data_type,
760+
format,
761+
} => {
762+
if let Some(format) = format {
763+
write!(f, "CAST({expr} AS {data_type} FORMAT {format})")
764+
} else {
765+
write!(f, "CAST({expr} AS {data_type})")
766+
}
767+
}
768+
Expr::TryCast {
769+
expr,
770+
data_type,
771+
format,
772+
} => {
773+
if let Some(format) = format {
774+
write!(f, "TRY_CAST({expr} AS {data_type} FORMAT {format})")
775+
} else {
776+
write!(f, "TRY_CAST({expr} AS {data_type})")
777+
}
778+
}
779+
Expr::SafeCast {
780+
expr,
781+
data_type,
782+
format,
783+
} => {
784+
if let Some(format) = format {
785+
write!(f, "SAFE_CAST({expr} AS {data_type} FORMAT {format})")
786+
} else {
787+
write!(f, "SAFE_CAST({expr} AS {data_type})")
788+
}
789+
}
757790
Expr::Extract { field, expr } => write!(f, "EXTRACT({field} FROM {expr})"),
758791
Expr::Ceil { expr, field } => {
759792
if field == &DateTimeField::NoDateTime {

src/parser/mod.rs

+28
Original file line numberDiff line numberDiff line change
@@ -1132,10 +1132,19 @@ impl<'a> Parser<'a> {
11321132
let expr = self.parse_expr()?;
11331133
self.expect_keyword(Keyword::AS)?;
11341134
let data_type = self.parse_data_type()?;
1135+
1136+
let format = if dialect_of!(self is BigQueryDialect) && self.parse_keyword(Keyword::FORMAT)
1137+
{
1138+
Some(self.parse_value()?)
1139+
} else {
1140+
None
1141+
};
1142+
11351143
self.expect_token(&Token::RParen)?;
11361144
Ok(Expr::Cast {
11371145
expr: Box::new(expr),
11381146
data_type,
1147+
format,
11391148
})
11401149
}
11411150

@@ -1145,10 +1154,19 @@ impl<'a> Parser<'a> {
11451154
let expr = self.parse_expr()?;
11461155
self.expect_keyword(Keyword::AS)?;
11471156
let data_type = self.parse_data_type()?;
1157+
1158+
let format = if dialect_of!(self is BigQueryDialect) && self.parse_keyword(Keyword::FORMAT)
1159+
{
1160+
Some(self.parse_value()?)
1161+
} else {
1162+
None
1163+
};
1164+
11481165
self.expect_token(&Token::RParen)?;
11491166
Ok(Expr::TryCast {
11501167
expr: Box::new(expr),
11511168
data_type,
1169+
format,
11521170
})
11531171
}
11541172

@@ -1158,10 +1176,19 @@ impl<'a> Parser<'a> {
11581176
let expr = self.parse_expr()?;
11591177
self.expect_keyword(Keyword::AS)?;
11601178
let data_type = self.parse_data_type()?;
1179+
1180+
let format = if dialect_of!(self is BigQueryDialect) && self.parse_keyword(Keyword::FORMAT)
1181+
{
1182+
Some(self.parse_value()?)
1183+
} else {
1184+
None
1185+
};
1186+
11611187
self.expect_token(&Token::RParen)?;
11621188
Ok(Expr::SafeCast {
11631189
expr: Box::new(expr),
11641190
data_type,
1191+
format,
11651192
})
11661193
}
11671194

@@ -2070,6 +2097,7 @@ impl<'a> Parser<'a> {
20702097
Ok(Expr::Cast {
20712098
expr: Box::new(expr),
20722099
data_type: self.parse_data_type()?,
2100+
format: None,
20732101
})
20742102
}
20752103

tests/sqlparser_bigquery.rs

+32
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,38 @@ fn parse_cast_type() {
222222
bigquery().verified_only_select(sql);
223223
}
224224

225+
#[test]
226+
fn parse_cast_date_format() {
227+
let sql =
228+
r#"SELECT CAST(date_valid_from AS DATE FORMAT 'YYYY-MM-DD') AS date_valid_from FROM foo"#;
229+
bigquery().verified_only_select(sql);
230+
}
231+
232+
#[test]
233+
fn parse_cast_time_format() {
234+
let sql = r#"SELECT CAST(TIME '21:30:00' AS STRING FORMAT 'PM') AS date_time_to_string"#;
235+
bigquery().verified_only_select(sql);
236+
}
237+
238+
#[test]
239+
#[ignore]
240+
fn parse_cast_timestamp_format_tz() {
241+
let sql = r#"SELECT CAST(TIMESTAMP '2008-12-25 00:00:00+00:00' AS STRING FORMAT 'TZH' AT TIME ZONE 'Asia/Kolkata') AS date_time_to_string"#;
242+
bigquery().verified_only_select(sql);
243+
}
244+
245+
#[test]
246+
fn parse_cast_string_to_bytes_format() {
247+
let sql = r#"SELECT CAST('Hello' AS BYTES FORMAT 'ASCII') AS string_to_bytes"#;
248+
bigquery().verified_only_select(sql);
249+
}
250+
251+
#[test]
252+
fn parse_cast_bytes_to_string_format() {
253+
let sql = r#"SELECT CAST(B'\x48\x65\x6c\x6c\x6f' AS STRING FORMAT 'ASCII') AS bytes_to_string"#;
254+
bigquery().verified_only_select(sql);
255+
}
256+
225257
#[test]
226258
fn parse_like() {
227259
fn chk(negated: bool) {

tests/sqlparser_common.rs

+10
Original file line numberDiff line numberDiff line change
@@ -1931,6 +1931,7 @@ fn parse_cast() {
19311931
&Expr::Cast {
19321932
expr: Box::new(Expr::Identifier(Ident::new("id"))),
19331933
data_type: DataType::BigInt(None),
1934+
format: None,
19341935
},
19351936
expr_from_projection(only(&select.projection))
19361937
);
@@ -1941,6 +1942,7 @@ fn parse_cast() {
19411942
&Expr::Cast {
19421943
expr: Box::new(Expr::Identifier(Ident::new("id"))),
19431944
data_type: DataType::TinyInt(None),
1945+
format: None,
19441946
},
19451947
expr_from_projection(only(&select.projection))
19461948
);
@@ -1967,6 +1969,7 @@ fn parse_cast() {
19671969
&Expr::Cast {
19681970
expr: Box::new(Expr::Identifier(Ident::new("id"))),
19691971
data_type: DataType::Nvarchar(Some(50)),
1972+
format: None,
19701973
},
19711974
expr_from_projection(only(&select.projection))
19721975
);
@@ -1977,6 +1980,7 @@ fn parse_cast() {
19771980
&Expr::Cast {
19781981
expr: Box::new(Expr::Identifier(Ident::new("id"))),
19791982
data_type: DataType::Clob(None),
1983+
format: None,
19801984
},
19811985
expr_from_projection(only(&select.projection))
19821986
);
@@ -1987,6 +1991,7 @@ fn parse_cast() {
19871991
&Expr::Cast {
19881992
expr: Box::new(Expr::Identifier(Ident::new("id"))),
19891993
data_type: DataType::Clob(Some(50)),
1994+
format: None,
19901995
},
19911996
expr_from_projection(only(&select.projection))
19921997
);
@@ -1997,6 +2002,7 @@ fn parse_cast() {
19972002
&Expr::Cast {
19982003
expr: Box::new(Expr::Identifier(Ident::new("id"))),
19992004
data_type: DataType::Binary(Some(50)),
2005+
format: None,
20002006
},
20012007
expr_from_projection(only(&select.projection))
20022008
);
@@ -2007,6 +2013,7 @@ fn parse_cast() {
20072013
&Expr::Cast {
20082014
expr: Box::new(Expr::Identifier(Ident::new("id"))),
20092015
data_type: DataType::Varbinary(Some(50)),
2016+
format: None,
20102017
},
20112018
expr_from_projection(only(&select.projection))
20122019
);
@@ -2017,6 +2024,7 @@ fn parse_cast() {
20172024
&Expr::Cast {
20182025
expr: Box::new(Expr::Identifier(Ident::new("id"))),
20192026
data_type: DataType::Blob(None),
2027+
format: None,
20202028
},
20212029
expr_from_projection(only(&select.projection))
20222030
);
@@ -2027,6 +2035,7 @@ fn parse_cast() {
20272035
&Expr::Cast {
20282036
expr: Box::new(Expr::Identifier(Ident::new("id"))),
20292037
data_type: DataType::Blob(Some(50)),
2038+
format: None,
20302039
},
20312040
expr_from_projection(only(&select.projection))
20322041
);
@@ -2040,6 +2049,7 @@ fn parse_try_cast() {
20402049
&Expr::TryCast {
20412050
expr: Box::new(Expr::Identifier(Ident::new("id"))),
20422051
data_type: DataType::BigInt(None),
2052+
format: None,
20432053
},
20442054
expr_from_projection(only(&select.projection))
20452055
);

tests/sqlparser_postgres.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1781,7 +1781,8 @@ fn parse_array_index_expr() {
17811781
})),
17821782
data_type: DataType::Array(Some(Box::new(DataType::Array(Some(Box::new(
17831783
DataType::Int(None)
1784-
))))))
1784+
)))))),
1785+
format: None,
17851786
}))),
17861787
indexes: vec![num[1].clone(), num[2].clone()],
17871788
},

tests/sqlparser_snowflake.rs

+1
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ fn parse_array() {
167167
&Expr::Cast {
168168
expr: Box::new(Expr::Identifier(Ident::new("a"))),
169169
data_type: DataType::Array(None),
170+
format: None,
170171
},
171172
expr_from_projection(only(&select.projection))
172173
);

0 commit comments

Comments
 (0)