BigQuery: support for ANY_VALUE HAVING clause (#1258)

This commit is contained in:
Joey Hain 2024-05-06 17:33:37 -07:00 committed by GitHub
parent a12a8882e7
commit c4f3ef9600
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 63 additions and 0 deletions

View file

@ -4962,6 +4962,15 @@ pub enum FunctionArgumentClause {
///
/// See <https://trino.io/docs/current/functions/aggregate.html>.
OnOverflow(ListAggOnOverflow),
/// Specifies a minimum or maximum bound on the input to [`ANY_VALUE`] on BigQuery.
///
/// Syntax:
/// ```plaintext
/// HAVING { MAX | MIN } expression
/// ```
///
/// [`ANY_VALUE`]: https://cloud.google.com/bigquery/docs/reference/standard-sql/aggregate_functions#any_value
Having(HavingBound),
/// The `SEPARATOR` clause to the [`GROUP_CONCAT`] function in MySQL.
///
/// [`GROUP_CONCAT`]: https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html#function_group-concat
@ -4979,6 +4988,7 @@ impl fmt::Display for FunctionArgumentClause {
}
FunctionArgumentClause::Limit(limit) => write!(f, "LIMIT {limit}"),
FunctionArgumentClause::OnOverflow(on_overflow) => write!(f, "{on_overflow}"),
FunctionArgumentClause::Having(bound) => write!(f, "{bound}"),
FunctionArgumentClause::Separator(sep) => write!(f, "SEPARATOR {sep}"),
}
}
@ -5087,6 +5097,35 @@ impl fmt::Display for ListAggOnOverflow {
}
}
/// The `HAVING` clause in a call to `ANY_VALUE` on BigQuery.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct HavingBound(pub HavingBoundKind, pub Expr);
impl fmt::Display for HavingBound {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "HAVING {} {}", self.0, self.1)
}
}
#[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 HavingBoundKind {
Min,
Max,
}
impl fmt::Display for HavingBoundKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
HavingBoundKind::Min => write!(f, "MIN"),
HavingBoundKind::Max => write!(f, "MAX"),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]

View file

@ -9534,6 +9534,20 @@ impl<'a> Parser<'a> {
clauses.push(FunctionArgumentClause::Limit(self.parse_expr()?));
}
if dialect_of!(self is GenericDialect | BigQueryDialect)
&& self.parse_keyword(Keyword::HAVING)
{
let kind = match self.expect_one_of_keywords(&[Keyword::MIN, Keyword::MAX])? {
Keyword::MIN => HavingBoundKind::Min,
Keyword::MAX => HavingBoundKind::Max,
_ => unreachable!(),
};
clauses.push(FunctionArgumentClause::Having(HavingBound(
kind,
self.parse_expr()?,
)))
}
if dialect_of!(self is GenericDialect | MySqlDialect)
&& self.parse_keyword(Keyword::SEPARATOR)
{

View file

@ -1924,3 +1924,13 @@ fn test_array_agg() {
.verified_expr("ARRAY_AGG(DISTINCT state IGNORE NULLS ORDER BY population DESC LIMIT 10)");
bigquery_and_generic().verified_expr("ARRAY_CONCAT_AGG(x ORDER BY ARRAY_LENGTH(x))");
}
#[test]
fn test_any_value() {
bigquery_and_generic().verified_expr("ANY_VALUE(fruit)");
bigquery_and_generic().verified_expr(
"ANY_VALUE(fruit) OVER (ORDER BY LENGTH(fruit) ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)",
);
bigquery_and_generic().verified_expr("ANY_VALUE(fruit HAVING MAX sold)");
bigquery_and_generic().verified_expr("ANY_VALUE(fruit HAVING MIN sold)");
}