Skip to content

Commit 758c307

Browse files
committed
Fix parsing error when having fields after nested struct in BigQuery
Before applying this patch, the following SQL cannot be parsed: ```SQL CREATE TABLE my_table ( f0 STRING, f1 STRUCT<a STRING, b STRUCT<c INT64, d STRING>>, f2 STRING, ) ``` But it's a valid SQL in BigQuery. The root cause is having comma after the trailing bracket will be reconigized as a mismatched closing.
1 parent b1b379e commit 758c307

File tree

2 files changed

+51
-9
lines changed

2 files changed

+51
-9
lines changed

src/parser/mod.rs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3034,7 +3034,6 @@ impl<'a> Parser<'a> {
30343034
where
30353035
F: FnMut(&mut Parser<'a>) -> Result<(StructField, MatchedTrailingBracket), ParserError>,
30363036
{
3037-
let start_token = self.peek_token();
30383037
self.expect_keyword_is(Keyword::STRUCT)?;
30393038

30403039
// Nothing to do if we have no type information.
@@ -3047,16 +3046,10 @@ impl<'a> Parser<'a> {
30473046
let trailing_bracket = loop {
30483047
let (def, trailing_bracket) = elem_parser(self)?;
30493048
field_defs.push(def);
3050-
if !self.consume_token(&Token::Comma) {
3049+
// it means that struct field definition is finished if we occurs `>>` or comma.
3050+
if trailing_bracket.0 || !self.consume_token(&Token::Comma) {
30513051
break trailing_bracket;
30523052
}
3053-
3054-
// Angle brackets are balanced so we only expect the trailing `>>` after
3055-
// we've matched all field types for the current struct.
3056-
// e.g. this is invalid syntax `STRUCT<STRUCT<INT>>>, INT>(NULL)`
3057-
if trailing_bracket.0 {
3058-
return parser_err!("unmatched > in STRUCT definition", start_token.span.start);
3059-
}
30603053
};
30613054

30623055
Ok((

tests/sqlparser_bigquery.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2472,3 +2472,52 @@ fn test_struct_field_options() {
24722472
")",
24732473
));
24742474
}
2475+
2476+
#[test]
2477+
fn test_struct_trailing_bracket() {
2478+
bigquery().verified_stmt(concat!(
2479+
"CREATE TABLE my_table (",
2480+
"f0 STRING, ",
2481+
"f1 STRUCT<a STRING, b STRUCT<c INT64, d STRING>>, ",
2482+
"f2 STRING",
2483+
")",
2484+
));
2485+
2486+
// More complex nested structs
2487+
bigquery().verified_stmt(concat!(
2488+
"CREATE TABLE my_table (",
2489+
"f0 STRING, ",
2490+
"f1 STRUCT<a STRING, b STRUCT<c INT64, d STRUCT<e STRING>>>, ",
2491+
"f2 STRUCT<h STRING, i STRUCT<j INT64, k STRUCT<l STRUCT<m STRING>>>>, ",
2492+
"f3 STRUCT<e STRING, f STRUCT<c INT64>>",
2493+
")",
2494+
));
2495+
2496+
// Bad case with missing closing bracket
2497+
assert_eq!(
2498+
ParserError::ParserError("Expected: >, found: )".to_owned()),
2499+
bigquery()
2500+
.parse_sql_statements("CREATE TABLE my_table(f1 STRUCT<a STRING, b INT64)")
2501+
.unwrap_err()
2502+
);
2503+
2504+
// Bad case with redundant closing bracket
2505+
assert_eq!(
2506+
ParserError::ParserError(
2507+
"unmatched > after parsing data type STRUCT<a STRING, b INT64>)".to_owned()
2508+
),
2509+
bigquery()
2510+
.parse_sql_statements("CREATE TABLE my_table(f1 STRUCT<a STRING, b INT64>>)")
2511+
.unwrap_err()
2512+
);
2513+
2514+
// Base case with redundant closing bracket in nested struct
2515+
assert_eq!(
2516+
ParserError::ParserError(
2517+
"Expected: ',' or ')' after column definition, found: >".to_owned()
2518+
),
2519+
bigquery()
2520+
.parse_sql_statements("CREATE TABLE my_table(f1 STRUCT<a STRUCT<b INT>>>, c INT64)")
2521+
.unwrap_err()
2522+
);
2523+
}

0 commit comments

Comments
 (0)