Skip to content

Commit ef69d8a

Browse files
committed
bigquery: CAST AS x FORMAT support
1 parent 19fca87 commit ef69d8a

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
@@ -1140,10 +1140,19 @@ impl<'a> Parser<'a> {
11401140
let expr = self.parse_expr()?;
11411141
self.expect_keyword(Keyword::AS)?;
11421142
let data_type = self.parse_data_type()?;
1143+
1144+
let format = if dialect_of!(self is BigQueryDialect) && self.parse_keyword(Keyword::FORMAT)
1145+
{
1146+
Some(self.parse_value()?)
1147+
} else {
1148+
None
1149+
};
1150+
11431151
self.expect_token(&Token::RParen)?;
11441152
Ok(Expr::Cast {
11451153
expr: Box::new(expr),
11461154
data_type,
1155+
format,
11471156
})
11481157
}
11491158

@@ -1153,10 +1162,19 @@ impl<'a> Parser<'a> {
11531162
let expr = self.parse_expr()?;
11541163
self.expect_keyword(Keyword::AS)?;
11551164
let data_type = self.parse_data_type()?;
1165+
1166+
let format = if dialect_of!(self is BigQueryDialect) && self.parse_keyword(Keyword::FORMAT)
1167+
{
1168+
Some(self.parse_value()?)
1169+
} else {
1170+
None
1171+
};
1172+
11561173
self.expect_token(&Token::RParen)?;
11571174
Ok(Expr::TryCast {
11581175
expr: Box::new(expr),
11591176
data_type,
1177+
format,
11601178
})
11611179
}
11621180

@@ -1166,10 +1184,19 @@ impl<'a> Parser<'a> {
11661184
let expr = self.parse_expr()?;
11671185
self.expect_keyword(Keyword::AS)?;
11681186
let data_type = self.parse_data_type()?;
1187+
1188+
let format = if dialect_of!(self is BigQueryDialect) && self.parse_keyword(Keyword::FORMAT)
1189+
{
1190+
Some(self.parse_value()?)
1191+
} else {
1192+
None
1193+
};
1194+
11691195
self.expect_token(&Token::RParen)?;
11701196
Ok(Expr::SafeCast {
11711197
expr: Box::new(expr),
11721198
data_type,
1199+
format,
11731200
})
11741201
}
11751202

@@ -2102,6 +2129,7 @@ impl<'a> Parser<'a> {
21022129
Ok(Expr::Cast {
21032130
expr: Box::new(expr),
21042131
data_type: self.parse_data_type()?,
2132+
format: None,
21052133
})
21062134
}
21072135

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
@@ -1936,6 +1936,7 @@ fn parse_cast() {
19361936
&Expr::Cast {
19371937
expr: Box::new(Expr::Identifier(Ident::new("id"))),
19381938
data_type: DataType::BigInt(None),
1939+
format: None,
19391940
},
19401941
expr_from_projection(only(&select.projection))
19411942
);
@@ -1946,6 +1947,7 @@ fn parse_cast() {
19461947
&Expr::Cast {
19471948
expr: Box::new(Expr::Identifier(Ident::new("id"))),
19481949
data_type: DataType::TinyInt(None),
1950+
format: None,
19491951
},
19501952
expr_from_projection(only(&select.projection))
19511953
);
@@ -1972,6 +1974,7 @@ fn parse_cast() {
19721974
&Expr::Cast {
19731975
expr: Box::new(Expr::Identifier(Ident::new("id"))),
19741976
data_type: DataType::Nvarchar(Some(50)),
1977+
format: None,
19751978
},
19761979
expr_from_projection(only(&select.projection))
19771980
);
@@ -1982,6 +1985,7 @@ fn parse_cast() {
19821985
&Expr::Cast {
19831986
expr: Box::new(Expr::Identifier(Ident::new("id"))),
19841987
data_type: DataType::Clob(None),
1988+
format: None,
19851989
},
19861990
expr_from_projection(only(&select.projection))
19871991
);
@@ -1992,6 +1996,7 @@ fn parse_cast() {
19921996
&Expr::Cast {
19931997
expr: Box::new(Expr::Identifier(Ident::new("id"))),
19941998
data_type: DataType::Clob(Some(50)),
1999+
format: None,
19952000
},
19962001
expr_from_projection(only(&select.projection))
19972002
);
@@ -2002,6 +2007,7 @@ fn parse_cast() {
20022007
&Expr::Cast {
20032008
expr: Box::new(Expr::Identifier(Ident::new("id"))),
20042009
data_type: DataType::Binary(Some(50)),
2010+
format: None,
20052011
},
20062012
expr_from_projection(only(&select.projection))
20072013
);
@@ -2012,6 +2018,7 @@ fn parse_cast() {
20122018
&Expr::Cast {
20132019
expr: Box::new(Expr::Identifier(Ident::new("id"))),
20142020
data_type: DataType::Varbinary(Some(50)),
2021+
format: None,
20152022
},
20162023
expr_from_projection(only(&select.projection))
20172024
);
@@ -2022,6 +2029,7 @@ fn parse_cast() {
20222029
&Expr::Cast {
20232030
expr: Box::new(Expr::Identifier(Ident::new("id"))),
20242031
data_type: DataType::Blob(None),
2032+
format: None,
20252033
},
20262034
expr_from_projection(only(&select.projection))
20272035
);
@@ -2032,6 +2040,7 @@ fn parse_cast() {
20322040
&Expr::Cast {
20332041
expr: Box::new(Expr::Identifier(Ident::new("id"))),
20342042
data_type: DataType::Blob(Some(50)),
2043+
format: None,
20352044
},
20362045
expr_from_projection(only(&select.projection))
20372046
);
@@ -2045,6 +2054,7 @@ fn parse_try_cast() {
20452054
&Expr::TryCast {
20462055
expr: Box::new(Expr::Identifier(Ident::new("id"))),
20472056
data_type: DataType::BigInt(None),
2057+
format: None,
20482058
},
20492059
expr_from_projection(only(&select.projection))
20502060
);

tests/sqlparser_postgres.rs

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

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)