Skip to content

Commit 706c56b

Browse files
iffyioayman-sigma
authored andcommitted
BigQuery: Support trailing commas in column definitions list (apache#1682)
1 parent 21c20b0 commit 706c56b

File tree

4 files changed

+65
-20
lines changed

4 files changed

+65
-20
lines changed

src/dialect/bigquery.rs

+5
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ impl Dialect for BigQueryDialect {
3131
true
3232
}
3333

34+
/// See <https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#create_table_statement>
35+
fn supports_column_definition_trailing_commas(&self) -> bool {
36+
true
37+
}
38+
3439
fn is_identifier_start(&self, ch: char) -> bool {
3540
ch.is_ascii_lowercase() || ch.is_ascii_uppercase() || ch == '_'
3641
}

src/dialect/mod.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -405,11 +405,18 @@ pub trait Dialect: Debug + Any {
405405
}
406406

407407
/// Returns true if the dialect supports trailing commas in the `FROM` clause of a `SELECT` statement.
408-
/// /// Example: `SELECT 1 FROM T, U, LIMIT 1`
408+
/// Example: `SELECT 1 FROM T, U, LIMIT 1`
409409
fn supports_from_trailing_commas(&self) -> bool {
410410
false
411411
}
412412

413+
/// Returns true if the dialect supports trailing commas in the
414+
/// column definitions list of a `CREATE` statement.
415+
/// Example: `CREATE TABLE T (x INT, y TEXT,)`
416+
fn supports_column_definition_trailing_commas(&self) -> bool {
417+
false
418+
}
419+
413420
/// Returns true if the dialect supports double dot notation for object names
414421
///
415422
/// Example

src/parser/mod.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -6728,7 +6728,11 @@ impl<'a> Parser<'a> {
67286728
return self.expected("',' or ')' after column definition", self.peek_token());
67296729
};
67306730

6731-
if rparen && (!comma || self.options.trailing_commas) {
6731+
if rparen
6732+
&& (!comma
6733+
|| self.dialect.supports_column_definition_trailing_commas()
6734+
|| self.options.trailing_commas)
6735+
{
67326736
let _ = self.consume_token(&Token::RParen);
67336737
break;
67346738
}
@@ -9308,7 +9312,11 @@ impl<'a> Parser<'a> {
93089312
self.next_token();
93099313
Ok(vec![])
93109314
} else {
9311-
let cols = self.parse_comma_separated(Parser::parse_view_column)?;
9315+
let cols = self.parse_comma_separated_with_trailing_commas(
9316+
Parser::parse_view_column,
9317+
self.dialect.supports_column_definition_trailing_commas(),
9318+
None,
9319+
)?;
93129320
self.expect_token(&Token::RParen)?;
93139321
Ok(cols)
93149322
}

tests/sqlparser_common.rs

+42-17
Original file line numberDiff line numberDiff line change
@@ -10192,15 +10192,19 @@ fn parse_trailing_comma() {
1019210192
"Expected: column name or constraint definition, found: )".to_string()
1019310193
)
1019410194
);
10195+
10196+
let unsupported_dialects = all_dialects_where(|d| !d.supports_trailing_commas());
10197+
assert_eq!(
10198+
unsupported_dialects
10199+
.parse_sql_statements("SELECT * FROM track ORDER BY milliseconds,")
10200+
.unwrap_err(),
10201+
ParserError::ParserError("Expected: an expression, found: EOF".to_string())
10202+
);
1019510203
}
1019610204

1019710205
#[test]
1019810206
fn parse_projection_trailing_comma() {
10199-
// Some dialects allow trailing commas only in the projection
10200-
let trailing_commas = TestedDialects::new(vec![
10201-
Box::new(SnowflakeDialect {}),
10202-
Box::new(BigQueryDialect {}),
10203-
]);
10207+
let trailing_commas = all_dialects_where(|d| d.supports_projection_trailing_commas());
1020410208

1020510209
trailing_commas.one_statement_parses_to(
1020610210
"SELECT album_id, name, FROM track",
@@ -10213,20 +10217,14 @@ fn parse_projection_trailing_comma() {
1021310217

1021410218
trailing_commas.verified_stmt("SELECT DISTINCT ON (album_id) name FROM track");
1021510219

10220+
let unsupported_dialects = all_dialects_where(|d| {
10221+
!d.supports_projection_trailing_commas() && !d.supports_trailing_commas()
10222+
});
1021610223
assert_eq!(
10217-
trailing_commas
10218-
.parse_sql_statements("SELECT * FROM track ORDER BY milliseconds,")
10224+
unsupported_dialects
10225+
.parse_sql_statements("SELECT album_id, name, FROM track")
1021910226
.unwrap_err(),
10220-
ParserError::ParserError("Expected: an expression, found: EOF".to_string())
10221-
);
10222-
10223-
assert_eq!(
10224-
trailing_commas
10225-
.parse_sql_statements("CREATE TABLE employees (name text, age int,)")
10226-
.unwrap_err(),
10227-
ParserError::ParserError(
10228-
"Expected: column name or constraint definition, found: )".to_string()
10229-
),
10227+
ParserError::ParserError("Expected an expression, found: FROM".to_string())
1023010228
);
1023110229
}
1023210230

@@ -13064,6 +13062,33 @@ fn parse_overlaps() {
1306413062
verified_stmt("SELECT (DATE '2016-01-10', DATE '2016-02-01') OVERLAPS (DATE '2016-01-20', DATE '2016-02-10')");
1306513063
}
1306613064

13065+
#[test]
13066+
fn parse_column_definition_trailing_commas() {
13067+
let dialects = all_dialects_where(|d| d.supports_column_definition_trailing_commas());
13068+
13069+
dialects.one_statement_parses_to("CREATE TABLE T (x INT64,)", "CREATE TABLE T (x INT64)");
13070+
dialects.one_statement_parses_to(
13071+
"CREATE TABLE T (x INT64, y INT64, )",
13072+
"CREATE TABLE T (x INT64, y INT64)",
13073+
);
13074+
dialects.one_statement_parses_to(
13075+
"CREATE VIEW T (x, y, ) AS SELECT 1",
13076+
"CREATE VIEW T (x, y) AS SELECT 1",
13077+
);
13078+
13079+
let unsupported_dialects = all_dialects_where(|d| {
13080+
!d.supports_projection_trailing_commas() && !d.supports_trailing_commas()
13081+
});
13082+
assert_eq!(
13083+
unsupported_dialects
13084+
.parse_sql_statements("CREATE TABLE employees (name text, age int,)")
13085+
.unwrap_err(),
13086+
ParserError::ParserError(
13087+
"Expected: column name or constraint definition, found: )".to_string()
13088+
),
13089+
);
13090+
}
13091+
1306713092
#[test]
1306813093
fn test_trailing_commas_in_from() {
1306913094
let dialects = all_dialects_where(|d| d.supports_from_trailing_commas());

0 commit comments

Comments
 (0)