Support DISTINCT AS { STRUCT | VALUE } for BigQuery (#1880)
Some checks failed
Rust / codestyle (push) Has been cancelled
Rust / lint (push) Has been cancelled
Rust / benchmark-lint (push) Has been cancelled
Rust / compile (push) Has been cancelled
Rust / docs (push) Has been cancelled
Rust / compile-no-std (push) Has been cancelled
Rust / test (beta) (push) Has been cancelled
Rust / test (nightly) (push) Has been cancelled
Rust / test (stable) (push) Has been cancelled

This commit is contained in:
Simon Sawert 2025-06-11 18:12:30 +02:00 committed by GitHub
parent c2e83d49f6
commit 703ba2cf48
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 69 additions and 18 deletions

View file

@ -3338,15 +3338,19 @@ impl fmt::Display for OpenJsonTableColumn {
}
/// BigQuery supports ValueTables which have 2 modes:
/// `SELECT AS STRUCT`
/// `SELECT AS VALUE`
/// `SELECT [ALL | DISTINCT] AS STRUCT`
/// `SELECT [ALL | DISTINCT] AS VALUE`
///
/// <https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#value_tables>
/// <https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#select_list>
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum ValueTableMode {
AsStruct,
AsValue,
DistinctAsStruct,
DistinctAsValue,
}
impl fmt::Display for ValueTableMode {
@ -3354,6 +3358,8 @@ impl fmt::Display for ValueTableMode {
match self {
ValueTableMode::AsStruct => write!(f, "AS STRUCT"),
ValueTableMode::AsValue => write!(f, "AS VALUE"),
ValueTableMode::DistinctAsStruct => write!(f, "DISTINCT AS STRUCT"),
ValueTableMode::DistinctAsValue => write!(f, "DISTINCT AS VALUE"),
}
}
}

View file

@ -11505,18 +11505,7 @@ impl<'a> Parser<'a> {
}
let select_token = self.expect_keyword(Keyword::SELECT)?;
let value_table_mode =
if dialect_of!(self is BigQueryDialect) && self.parse_keyword(Keyword::AS) {
if self.parse_keyword(Keyword::VALUE) {
Some(ValueTableMode::AsValue)
} else if self.parse_keyword(Keyword::STRUCT) {
Some(ValueTableMode::AsStruct)
} else {
self.expected("VALUE or STRUCT", self.peek_token())?
}
} else {
None
};
let value_table_mode = self.parse_value_table_mode()?;
let mut top_before_distinct = false;
let mut top = None;
@ -11692,6 +11681,32 @@ impl<'a> Parser<'a> {
})
}
fn parse_value_table_mode(&mut self) -> Result<Option<ValueTableMode>, ParserError> {
if !dialect_of!(self is BigQueryDialect) {
return Ok(None);
}
let mode = if self.parse_keywords(&[Keyword::DISTINCT, Keyword::AS, Keyword::VALUE]) {
Some(ValueTableMode::DistinctAsValue)
} else if self.parse_keywords(&[Keyword::DISTINCT, Keyword::AS, Keyword::STRUCT]) {
Some(ValueTableMode::DistinctAsStruct)
} else if self.parse_keywords(&[Keyword::AS, Keyword::VALUE])
|| self.parse_keywords(&[Keyword::ALL, Keyword::AS, Keyword::VALUE])
{
Some(ValueTableMode::AsValue)
} else if self.parse_keywords(&[Keyword::AS, Keyword::STRUCT])
|| self.parse_keywords(&[Keyword::ALL, Keyword::AS, Keyword::STRUCT])
{
Some(ValueTableMode::AsStruct)
} else if self.parse_keyword(Keyword::AS) {
self.expected("VALUE or STRUCT", self.peek_token())?
} else {
None
};
Ok(mode)
}
/// Invoke `f` after first setting the parser's `ParserState` to `state`.
///
/// Upon return, restores the parser's state to what it started at.

View file

@ -2313,16 +2313,46 @@ fn bigquery_select_expr_star() {
#[test]
fn test_select_as_struct() {
bigquery().verified_only_select("SELECT * FROM (SELECT AS VALUE STRUCT(123 AS a, false AS b))");
for (sql, parse_to) in [
(
"SELECT * FROM (SELECT AS STRUCT STRUCT(123 AS a, false AS b))",
"SELECT * FROM (SELECT AS STRUCT STRUCT(123 AS a, false AS b))",
),
(
"SELECT * FROM (SELECT DISTINCT AS STRUCT STRUCT(123 AS a, false AS b))",
"SELECT * FROM (SELECT DISTINCT AS STRUCT STRUCT(123 AS a, false AS b))",
),
(
"SELECT * FROM (SELECT ALL AS STRUCT STRUCT(123 AS a, false AS b))",
"SELECT * FROM (SELECT AS STRUCT STRUCT(123 AS a, false AS b))",
),
] {
bigquery().one_statement_parses_to(sql, parse_to);
}
let select = bigquery().verified_only_select("SELECT AS STRUCT 1 AS a, 2 AS b");
assert_eq!(Some(ValueTableMode::AsStruct), select.value_table_mode);
}
#[test]
fn test_select_as_value() {
bigquery().verified_only_select(
"SELECT * FROM (SELECT AS VALUE STRUCT(5 AS star_rating, false AS up_down_rating))",
);
for (sql, parse_to) in [
(
"SELECT * FROM (SELECT AS VALUE STRUCT(5 AS star_rating, false AS up_down_rating))",
"SELECT * FROM (SELECT AS VALUE STRUCT(5 AS star_rating, false AS up_down_rating))",
),
(
"SELECT * FROM (SELECT DISTINCT AS VALUE STRUCT(5 AS star_rating, false AS up_down_rating))",
"SELECT * FROM (SELECT DISTINCT AS VALUE STRUCT(5 AS star_rating, false AS up_down_rating))",
),
(
"SELECT * FROM (SELECT ALL AS VALUE STRUCT(5 AS star_rating, false AS up_down_rating))",
"SELECT * FROM (SELECT AS VALUE STRUCT(5 AS star_rating, false AS up_down_rating))",
),
] {
bigquery().one_statement_parses_to(sql, parse_to);
}
let select = bigquery().verified_only_select("SELECT AS VALUE STRUCT(1 AS a, 2 AS b) AS xyz");
assert_eq!(Some(ValueTableMode::AsValue), select.value_table_mode);
}