Skip to content

Commit 185a490

Browse files
authored
Fix parsing error when having fields after nested struct in BigQuery (apache#1897)
1 parent b1b379e commit 185a490

File tree

2 files changed

+77
-38
lines changed

2 files changed

+77
-38
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+
// The struct field definition is finished if it 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: 75 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -635,35 +635,6 @@ fn parse_nested_data_types() {
635635
}
636636
}
637637

638-
#[test]
639-
fn parse_invalid_brackets() {
640-
let sql = "SELECT STRUCT<INT64>>(NULL)";
641-
assert_eq!(
642-
bigquery_and_generic()
643-
.parse_sql_statements(sql)
644-
.unwrap_err(),
645-
ParserError::ParserError("unmatched > in STRUCT literal".to_string())
646-
);
647-
648-
let sql = "SELECT STRUCT<STRUCT<INT64>>>(NULL)";
649-
assert_eq!(
650-
bigquery_and_generic()
651-
.parse_sql_statements(sql)
652-
.unwrap_err(),
653-
ParserError::ParserError("Expected: (, found: >".to_string())
654-
);
655-
656-
let sql = "CREATE TABLE table (x STRUCT<STRUCT<INT64>>>)";
657-
assert_eq!(
658-
bigquery_and_generic()
659-
.parse_sql_statements(sql)
660-
.unwrap_err(),
661-
ParserError::ParserError(
662-
"Expected: ',' or ')' after column definition, found: >".to_string()
663-
)
664-
);
665-
}
666-
667638
#[test]
668639
fn parse_tuple_struct_literal() {
669640
// tuple syntax: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#tuple_syntax
@@ -2472,3 +2443,78 @@ fn test_struct_field_options() {
24722443
")",
24732444
));
24742445
}
2446+
2447+
#[test]
2448+
fn test_struct_trailing_and_nested_bracket() {
2449+
bigquery().verified_stmt(concat!(
2450+
"CREATE TABLE my_table (",
2451+
"f0 STRING, ",
2452+
"f1 STRUCT<a STRING, b STRUCT<c INT64, d STRING>>, ",
2453+
"f2 STRING",
2454+
")",
2455+
));
2456+
2457+
// More complex nested structs
2458+
bigquery().verified_stmt(concat!(
2459+
"CREATE TABLE my_table (",
2460+
"f0 STRING, ",
2461+
"f1 STRUCT<a STRING, b STRUCT<c INT64, d STRUCT<e STRING>>>, ",
2462+
"f2 STRUCT<h STRING, i STRUCT<j INT64, k STRUCT<l STRUCT<m STRING>>>>, ",
2463+
"f3 STRUCT<e STRING, f STRUCT<c INT64>>",
2464+
")",
2465+
));
2466+
2467+
// Bad case with missing closing bracket
2468+
assert_eq!(
2469+
ParserError::ParserError("Expected: >, found: )".to_owned()),
2470+
bigquery()
2471+
.parse_sql_statements("CREATE TABLE my_table(f1 STRUCT<a STRING, b INT64)")
2472+
.unwrap_err()
2473+
);
2474+
2475+
// Bad case with redundant closing bracket
2476+
assert_eq!(
2477+
ParserError::ParserError(
2478+
"unmatched > after parsing data type STRUCT<a STRING, b INT64>)".to_owned()
2479+
),
2480+
bigquery()
2481+
.parse_sql_statements("CREATE TABLE my_table(f1 STRUCT<a STRING, b INT64>>)")
2482+
.unwrap_err()
2483+
);
2484+
2485+
// Base case with redundant closing bracket in nested struct
2486+
assert_eq!(
2487+
ParserError::ParserError(
2488+
"Expected: ',' or ')' after column definition, found: >".to_owned()
2489+
),
2490+
bigquery()
2491+
.parse_sql_statements("CREATE TABLE my_table(f1 STRUCT<a STRUCT<b INT>>>, c INT64)")
2492+
.unwrap_err()
2493+
);
2494+
2495+
let sql = "SELECT STRUCT<INT64>>(NULL)";
2496+
assert_eq!(
2497+
bigquery_and_generic()
2498+
.parse_sql_statements(sql)
2499+
.unwrap_err(),
2500+
ParserError::ParserError("unmatched > in STRUCT literal".to_string())
2501+
);
2502+
2503+
let sql = "SELECT STRUCT<STRUCT<INT64>>>(NULL)";
2504+
assert_eq!(
2505+
bigquery_and_generic()
2506+
.parse_sql_statements(sql)
2507+
.unwrap_err(),
2508+
ParserError::ParserError("Expected: (, found: >".to_string())
2509+
);
2510+
2511+
let sql = "CREATE TABLE table (x STRUCT<STRUCT<INT64>>>)";
2512+
assert_eq!(
2513+
bigquery_and_generic()
2514+
.parse_sql_statements(sql)
2515+
.unwrap_err(),
2516+
ParserError::ParserError(
2517+
"Expected: ',' or ')' after column definition, found: >".to_string()
2518+
)
2519+
);
2520+
}

0 commit comments

Comments
 (0)