Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8265,6 +8265,12 @@ pub enum FunctionArgumentClause {
///
/// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/navigation_functions#first_value
IgnoreOrRespectNulls(NullTreatment),
/// The inline `WHERE` filter clause on a GoogleSQL aggregate, e.g.
/// `COUNT(* WHERE cond)` / `SUM(x WHERE cond)` / `ARRAY_AGG(x WHERE cond ORDER BY ..)`.
/// Equivalent to the standard `AGG(x) FILTER (WHERE cond)`.
///
/// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/aggregate_functions#grouping_and_filtering
Where(Expr),
/// Specifies the the ordering for some ordered set aggregates, e.g. `ARRAY_AGG` on [BigQuery].
///
/// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/aggregate_functions#array_agg
Expand Down Expand Up @@ -8306,6 +8312,7 @@ impl fmt::Display for FunctionArgumentClause {
FunctionArgumentClause::IgnoreOrRespectNulls(null_treatment) => {
write!(f, "{null_treatment}")
}
FunctionArgumentClause::Where(expr) => write!(f, "WHERE {expr}"),
FunctionArgumentClause::OrderBy(order_by) => {
write!(f, "ORDER BY {}", display_comma_separated(order_by))
}
Expand Down
1 change: 1 addition & 0 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1794,6 +1794,7 @@ impl Spanned for FunctionArgumentClause {
fn span(&self) -> Span {
match self {
FunctionArgumentClause::IgnoreOrRespectNulls(_) => Span::empty(),
FunctionArgumentClause::Where(expr) => expr.span(),
FunctionArgumentClause::OrderBy(vec) => union_spans(vec.iter().map(|i| i.expr.span())),
FunctionArgumentClause::Limit(expr) => expr.span(),
FunctionArgumentClause::OnOverflow(_) => Span::empty(),
Expand Down
8 changes: 8 additions & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18502,6 +18502,14 @@ impl<'a> Parser<'a> {
let duplicate_treatment = self.parse_duplicate_treatment()?;
let args = self.parse_comma_separated(Parser::parse_function_args)?;

// GoogleSQL inline aggregate filter: `AGG(expr WHERE cond [ORDER BY ..])`.
// Precedes ORDER BY / LIMIT / HAVING; equivalent to `AGG(expr) FILTER (WHERE cond)`.
if dialect_of!(self is GenericDialect | BigQueryDialect)
&& self.parse_keyword(Keyword::WHERE)
{
clauses.push(FunctionArgumentClause::Where(self.parse_expr()?));
}

if self.dialect.supports_window_function_null_treatment_arg() {
if let Some(null_treatment) = self.parse_null_treatment()? {
clauses.push(FunctionArgumentClause::IgnoreOrRespectNulls(null_treatment));
Expand Down
25 changes: 25 additions & 0 deletions tests/sqlparser_bigquery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2950,3 +2950,28 @@ fn test_create_snapshot_table() {
"CREATE SNAPSHOT TABLE IF NOT EXISTS dataset_id.table1 CLONE dataset_id.table2 FOR SYSTEM_TIME AS OF TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 HOUR) OPTIONS(expiration_timestamp = TIMESTAMP '2025-01-01 00:00:00 UTC')",
);
}

#[test]
fn parse_aggregate_with_where_filter() {
// GoogleSQL allows an inline `WHERE` filter inside an aggregate call,
// e.g. `COUNT(* WHERE cond)` / `SUM(x WHERE cond)`. It is the in-argument
// spelling of the standard `AGG(x) FILTER (WHERE cond)`. Round-trips via
// the `FunctionArgumentClause::Where` clause.
bigquery().verified_stmt("SELECT COUNT(* WHERE x > 1) FROM t");
bigquery().verified_stmt("SELECT SUM(x WHERE y > 0) FROM t");
// Co-occurs with (and precedes) an in-argument ORDER BY.
bigquery().verified_stmt("SELECT ARRAY_AGG(x WHERE x > 100 ORDER BY x DESC) FROM t");

// The filter is captured as a FunctionArgumentClause::Where.
let select = bigquery().verified_only_select("SELECT SUM(salary WHERE dept = 1) FROM emp");
let SelectItem::UnnamedExpr(Expr::Function(func)) = &select.projection[0] else {
panic!("expected a function projection");
};
let FunctionArguments::List(list) = &func.args else {
panic!("expected a function argument list");
};
assert!(list
.clauses
.iter()
.any(|c| matches!(c, FunctionArgumentClause::Where(_))));
}