Skip to content

Commit 703ba2c

Browse files
authored
Support DISTINCT AS { STRUCT | VALUE } for BigQuery (#1880)
1 parent c2e83d4 commit 703ba2c

File tree

3 files changed

+69
-18
lines changed

3 files changed

+69
-18
lines changed

src/ast/query.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3338,22 +3338,28 @@ impl fmt::Display for OpenJsonTableColumn {
33383338
}
33393339

33403340
/// BigQuery supports ValueTables which have 2 modes:
3341-
/// `SELECT AS STRUCT`
3342-
/// `SELECT AS VALUE`
3341+
/// `SELECT [ALL | DISTINCT] AS STRUCT`
3342+
/// `SELECT [ALL | DISTINCT] AS VALUE`
3343+
///
33433344
/// <https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#value_tables>
3345+
/// <https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#select_list>
33443346
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
33453347
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
33463348
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
33473349
pub enum ValueTableMode {
33483350
AsStruct,
33493351
AsValue,
3352+
DistinctAsStruct,
3353+
DistinctAsValue,
33503354
}
33513355

33523356
impl fmt::Display for ValueTableMode {
33533357
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
33543358
match self {
33553359
ValueTableMode::AsStruct => write!(f, "AS STRUCT"),
33563360
ValueTableMode::AsValue => write!(f, "AS VALUE"),
3361+
ValueTableMode::DistinctAsStruct => write!(f, "DISTINCT AS STRUCT"),
3362+
ValueTableMode::DistinctAsValue => write!(f, "DISTINCT AS VALUE"),
33573363
}
33583364
}
33593365
}

src/parser/mod.rs

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

1150711507
let select_token = self.expect_keyword(Keyword::SELECT)?;
11508-
let value_table_mode =
11509-
if dialect_of!(self is BigQueryDialect) && self.parse_keyword(Keyword::AS) {
11510-
if self.parse_keyword(Keyword::VALUE) {
11511-
Some(ValueTableMode::AsValue)
11512-
} else if self.parse_keyword(Keyword::STRUCT) {
11513-
Some(ValueTableMode::AsStruct)
11514-
} else {
11515-
self.expected("VALUE or STRUCT", self.peek_token())?
11516-
}
11517-
} else {
11518-
None
11519-
};
11508+
let value_table_mode = self.parse_value_table_mode()?;
1152011509

1152111510
let mut top_before_distinct = false;
1152211511
let mut top = None;
@@ -11692,6 +11681,32 @@ impl<'a> Parser<'a> {
1169211681
})
1169311682
}
1169411683

11684+
fn parse_value_table_mode(&mut self) -> Result<Option<ValueTableMode>, ParserError> {
11685+
if !dialect_of!(self is BigQueryDialect) {
11686+
return Ok(None);
11687+
}
11688+
11689+
let mode = if self.parse_keywords(&[Keyword::DISTINCT, Keyword::AS, Keyword::VALUE]) {
11690+
Some(ValueTableMode::DistinctAsValue)
11691+
} else if self.parse_keywords(&[Keyword::DISTINCT, Keyword::AS, Keyword::STRUCT]) {
11692+
Some(ValueTableMode::DistinctAsStruct)
11693+
} else if self.parse_keywords(&[Keyword::AS, Keyword::VALUE])
11694+
|| self.parse_keywords(&[Keyword::ALL, Keyword::AS, Keyword::VALUE])
11695+
{
11696+
Some(ValueTableMode::AsValue)
11697+
} else if self.parse_keywords(&[Keyword::AS, Keyword::STRUCT])
11698+
|| self.parse_keywords(&[Keyword::ALL, Keyword::AS, Keyword::STRUCT])
11699+
{
11700+
Some(ValueTableMode::AsStruct)
11701+
} else if self.parse_keyword(Keyword::AS) {
11702+
self.expected("VALUE or STRUCT", self.peek_token())?
11703+
} else {
11704+
None
11705+
};
11706+
11707+
Ok(mode)
11708+
}
11709+
1169511710
/// Invoke `f` after first setting the parser's `ParserState` to `state`.
1169611711
///
1169711712
/// Upon return, restores the parser's state to what it started at.

tests/sqlparser_bigquery.rs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2313,16 +2313,46 @@ fn bigquery_select_expr_star() {
23132313

23142314
#[test]
23152315
fn test_select_as_struct() {
2316-
bigquery().verified_only_select("SELECT * FROM (SELECT AS VALUE STRUCT(123 AS a, false AS b))");
2316+
for (sql, parse_to) in [
2317+
(
2318+
"SELECT * FROM (SELECT AS STRUCT STRUCT(123 AS a, false AS b))",
2319+
"SELECT * FROM (SELECT AS STRUCT STRUCT(123 AS a, false AS b))",
2320+
),
2321+
(
2322+
"SELECT * FROM (SELECT DISTINCT AS STRUCT STRUCT(123 AS a, false AS b))",
2323+
"SELECT * FROM (SELECT DISTINCT AS STRUCT STRUCT(123 AS a, false AS b))",
2324+
),
2325+
(
2326+
"SELECT * FROM (SELECT ALL AS STRUCT STRUCT(123 AS a, false AS b))",
2327+
"SELECT * FROM (SELECT AS STRUCT STRUCT(123 AS a, false AS b))",
2328+
),
2329+
] {
2330+
bigquery().one_statement_parses_to(sql, parse_to);
2331+
}
2332+
23172333
let select = bigquery().verified_only_select("SELECT AS STRUCT 1 AS a, 2 AS b");
23182334
assert_eq!(Some(ValueTableMode::AsStruct), select.value_table_mode);
23192335
}
23202336

23212337
#[test]
23222338
fn test_select_as_value() {
2323-
bigquery().verified_only_select(
2324-
"SELECT * FROM (SELECT AS VALUE STRUCT(5 AS star_rating, false AS up_down_rating))",
2325-
);
2339+
for (sql, parse_to) in [
2340+
(
2341+
"SELECT * FROM (SELECT AS VALUE STRUCT(5 AS star_rating, false AS up_down_rating))",
2342+
"SELECT * FROM (SELECT AS VALUE STRUCT(5 AS star_rating, false AS up_down_rating))",
2343+
),
2344+
(
2345+
"SELECT * FROM (SELECT DISTINCT AS VALUE STRUCT(5 AS star_rating, false AS up_down_rating))",
2346+
"SELECT * FROM (SELECT DISTINCT AS VALUE STRUCT(5 AS star_rating, false AS up_down_rating))",
2347+
),
2348+
(
2349+
"SELECT * FROM (SELECT ALL AS VALUE STRUCT(5 AS star_rating, false AS up_down_rating))",
2350+
"SELECT * FROM (SELECT AS VALUE STRUCT(5 AS star_rating, false AS up_down_rating))",
2351+
),
2352+
] {
2353+
bigquery().one_statement_parses_to(sql, parse_to);
2354+
}
2355+
23262356
let select = bigquery().verified_only_select("SELECT AS VALUE STRUCT(1 AS a, 2 AS b) AS xyz");
23272357
assert_eq!(Some(ValueTableMode::AsValue), select.value_table_mode);
23282358
}

0 commit comments

Comments
 (0)