Skip to content

Commit 12cba60

Browse files
committed
Support DISTINCT AS { STRUCT | VALUE } for BigQuery
According to the query syntax at https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#select_list you can combine `[ { ALL | DISTINCT } ]` with `[ AS { STRUCT | VALUE } ]`: ```sh SELECT [ WITH differential_privacy_clause ] [ { ALL | DISTINCT } ] [ AS { STRUCT | VALUE } ] select_list ``` This adds support to parse `DISTINCT` or `ALL` as the first keyword after `SELECT` and adds two new variants to the `ValueTableMode` if defined.
1 parent 9fc9009 commit 12cba60

File tree

3 files changed

+43
-3
lines changed

3 files changed

+43
-3
lines changed

src/ast/query.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3340,20 +3340,29 @@ impl fmt::Display for OpenJsonTableColumn {
33403340
/// BigQuery supports ValueTables which have 2 modes:
33413341
/// `SELECT AS STRUCT`
33423342
/// `SELECT AS VALUE`
3343+
///
3344+
/// They can be combined with `[ { ALL | DISTINCT } ]`, e.g.
3345+
/// `SELECT DISTINCT AS STRUCT`
3346+
///
33433347
/// <https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#value_tables>
3348+
/// <https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#select_list>
33443349
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
33453350
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
33463351
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
33473352
pub enum ValueTableMode {
33483353
AsStruct,
33493354
AsValue,
3355+
DistinctAsStruct,
3356+
DistinctAsValue,
33503357
}
33513358

33523359
impl fmt::Display for ValueTableMode {
33533360
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
33543361
match self {
33553362
ValueTableMode::AsStruct => write!(f, "AS STRUCT"),
33563363
ValueTableMode::AsValue => write!(f, "AS VALUE"),
3364+
ValueTableMode::DistinctAsStruct => write!(f, "DISTINCT AS STRUCT"),
3365+
ValueTableMode::DistinctAsValue => write!(f, "DISTINCT AS VALUE"),
33573366
}
33583367
}
33593368
}

src/parser/mod.rs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11505,12 +11505,22 @@ impl<'a> Parser<'a> {
1150511505
}
1150611506

1150711507
let select_token = self.expect_keyword(Keyword::SELECT)?;
11508+
let distinct_pre_as = self.parse_all_or_distinct()?;
11509+
1150811510
let value_table_mode =
1150911511
if dialect_of!(self is BigQueryDialect) && self.parse_keyword(Keyword::AS) {
1151011512
if self.parse_keyword(Keyword::VALUE) {
11511-
Some(ValueTableMode::AsValue)
11513+
if distinct_pre_as.is_some() {
11514+
Some(ValueTableMode::DistinctAsValue)
11515+
} else {
11516+
Some(ValueTableMode::AsValue)
11517+
}
1151211518
} else if self.parse_keyword(Keyword::STRUCT) {
11513-
Some(ValueTableMode::AsStruct)
11519+
if distinct_pre_as.is_some() {
11520+
Some(ValueTableMode::DistinctAsStruct)
11521+
} else {
11522+
Some(ValueTableMode::AsStruct)
11523+
}
1151411524
} else {
1151511525
self.expected("VALUE or STRUCT", self.peek_token())?
1151611526
}
@@ -11524,7 +11534,17 @@ impl<'a> Parser<'a> {
1152411534
top = Some(self.parse_top()?);
1152511535
top_before_distinct = true;
1152611536
}
11527-
let distinct = self.parse_all_or_distinct()?;
11537+
11538+
// If we parsed a `DISTINCT` value before checking `ValueTableMode` and it is set to some,
11539+
// but we didn't have an `AS`, this is the initial `DISTINCT` value in the `SELECT` and
11540+
// should be re-used.
11541+
// If we don't have a `DISTINCT` parsed or if it was consumed for the `ValueTableMode` we
11542+
// look for `DISTINCT` again.
11543+
let distinct = if value_table_mode.is_none() && distinct_pre_as.is_some() {
11544+
distinct_pre_as
11545+
} else {
11546+
self.parse_all_or_distinct()?
11547+
};
1152811548
if !self.dialect.supports_top_before_distinct() && self.parse_keyword(Keyword::TOP) {
1152911549
top = Some(self.parse_top()?);
1153011550
}

tests/sqlparser_bigquery.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2377,3 +2377,14 @@ fn test_any_type() {
23772377
fn test_any_type_dont_break_custom_type() {
23782378
bigquery_and_generic().verified_stmt("CREATE TABLE foo (x ANY)");
23792379
}
2380+
2381+
#[test]
2382+
fn test_select_distinct_as_struct_or_value() {
2383+
for sql in [
2384+
"SELECT DISTINCT AS STRUCT a, ABS(b) FROM UNNEST(c) AS T",
2385+
"SELECT DISTINCT AS VALUE a, ABS(b) FROM UNNEST(c) AS T",
2386+
"SELECT ARRAY(SELECT DISTINCT AS STRUCT a, b, ABS(c) AS c, ABS(d) AS d FROM UNNEST(e) AS T)",
2387+
] {
2388+
bigquery().verified_stmt(sql);
2389+
}
2390+
}

0 commit comments

Comments
 (0)