Consolidate representation of function calls, remove AggregateExpressionWithFilter, ArraySubquery, ListAgg and ArrayAgg (#1247)

Co-authored-by: Andrew Lamb <andrew@nerdnetworks.org>
This commit is contained in:
Joey Hain 2024-05-03 11:46:03 -07:00 committed by GitHub
parent 5ea9c01bb2
commit a14faa36bb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 773 additions and 1040 deletions

View file

@ -13,16 +13,12 @@
//! SQL Abstract Syntax Tree (AST) types
#[cfg(not(feature = "std"))]
use alloc::{
borrow::Cow,
boxed::Box,
format,
string::{String, ToString},
vec::Vec,
};
#[cfg(feature = "std")]
use std::borrow::Cow;
use core::fmt::{self, Display};
#[cfg(feature = "serde")]
@ -690,11 +686,6 @@ pub enum Expr {
},
/// Scalar function call e.g. `LEFT(foo, 5)`
Function(Function),
/// Aggregate function with filter
AggregateExpressionWithFilter {
expr: Box<Expr>,
filter: Box<Expr>,
},
/// `CASE [<operand>] WHEN <condition> THEN <result> ... [ELSE <result>] END`
///
/// Note we only recognize a complete single expression as `<condition>`,
@ -715,12 +706,6 @@ pub enum Expr {
/// A parenthesized subquery `(SELECT ...)`, used in expression like
/// `SELECT (subquery) AS x` or `WHERE (subquery) = x`
Subquery(Box<Query>),
/// An array subquery constructor, e.g. `SELECT ARRAY(SELECT 1 UNION SELECT 2)`
ArraySubquery(Box<Query>),
/// The `LISTAGG` function `SELECT LISTAGG(...) WITHIN GROUP (ORDER BY ...)`
ListAgg(ListAgg),
/// The `ARRAY_AGG` function `SELECT ARRAY_AGG(... ORDER BY ...)`
ArrayAgg(ArrayAgg),
/// The `GROUPING SETS` expr.
GroupingSets(Vec<Vec<Expr>>),
/// The `CUBE` expr.
@ -1064,9 +1049,6 @@ impl fmt::Display for Expr {
write!(f, " '{}'", &value::escape_single_quote_string(value))
}
Expr::Function(fun) => write!(f, "{fun}"),
Expr::AggregateExpressionWithFilter { expr, filter } => {
write!(f, "{expr} FILTER (WHERE {filter})")
}
Expr::Case {
operand,
conditions,
@ -1093,9 +1075,6 @@ impl fmt::Display for Expr {
subquery
),
Expr::Subquery(s) => write!(f, "({s})"),
Expr::ArraySubquery(s) => write!(f, "ARRAY({s})"),
Expr::ListAgg(listagg) => write!(f, "{listagg}"),
Expr::ArrayAgg(arrayagg) => write!(f, "{arrayagg}"),
Expr::GroupingSets(sets) => {
write!(f, "GROUPING SETS (")?;
let mut sep = "";
@ -1411,35 +1390,6 @@ impl fmt::Display for NullTreatment {
}
}
/// Specifies Ignore / Respect NULL within window functions.
#[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 NullTreatmentType {
/// The declaration is part of the function's arguments.
///
/// ```sql
/// FIRST_VALUE(x IGNORE NULLS) OVER ()
/// ```
FunctionArg(NullTreatment),
/// The declaration occurs after the function call.
///
/// ```sql
/// FIRST_VALUE(x) IGNORE NULLS OVER ()
/// ```
AfterFunction(NullTreatment),
}
impl Display for NullTreatmentType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let null_treatment = match self {
NullTreatmentType::FunctionArg(n) => n,
NullTreatmentType::AfterFunction(n) => n,
};
write!(f, "{null_treatment}")
}
}
/// Specifies [WindowFrame]'s `start_bound` and `end_bound`
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@ -4829,22 +4779,169 @@ impl fmt::Display for CloseCursor {
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Function {
pub name: ObjectName,
pub args: Vec<FunctionArg>,
/// The arguments to the function, including any options specified within the
/// delimiting parentheses.
pub args: FunctionArguments,
/// e.g. `x > 5` in `COUNT(x) FILTER (WHERE x > 5)`
pub filter: Option<Box<Expr>>,
/// Specifies Ignore / Respect NULL within window functions.
/// Indicates how `NULL`s should be handled in the calculation.
///
/// Example:
/// ```plaintext
/// FIRST_VALUE( <expr> ) [ { IGNORE | RESPECT } NULLS ] OVER ...
/// ```
///
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/navigation_functions#first_value)
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/functions/first_value)
pub null_treatment: Option<NullTreatmentType>,
pub null_treatment: Option<NullTreatment>,
/// The `OVER` clause, indicating a window function call.
pub over: Option<WindowType>,
/// aggregate functions may specify eg `COUNT(DISTINCT x)`
pub distinct: bool,
/// Some functions must be called without trailing parentheses, for example Postgres
/// do it for current_catalog, current_schema, etc. This flags is used for formatting.
pub special: bool,
/// Required ordering for the function (if empty, there is no requirement).
pub order_by: Vec<OrderByExpr>,
/// A clause used with certain aggregate functions to control the ordering
/// within grouped sets before the function is applied.
///
/// Syntax:
/// ```plaintext
/// <aggregate_function>(expression) WITHIN GROUP (ORDER BY key [ASC | DESC], ...)
/// ```
pub within_group: Vec<OrderByExpr>,
}
impl fmt::Display for Function {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}{}", self.name, self.args)?;
if !self.within_group.is_empty() {
write!(
f,
" WITHIN GROUP (ORDER BY {})",
display_comma_separated(&self.within_group)
)?;
}
if let Some(filter_cond) = &self.filter {
write!(f, " FILTER (WHERE {filter_cond})")?;
}
if let Some(null_treatment) = &self.null_treatment {
write!(f, " {null_treatment}")?;
}
if let Some(o) = &self.over {
write!(f, " OVER {o}")?;
}
Ok(())
}
}
/// The arguments passed to a function call.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FunctionArguments {
/// Used for special functions like `CURRENT_TIMESTAMP` that are invoked
/// without parentheses.
None,
/// On some dialects, a subquery can be passed without surrounding
/// parentheses if it's the sole argument to the function.
Subquery(Box<Query>),
/// A normal function argument list, including any clauses within it such as
/// `DISTINCT` or `ORDER BY`.
List(FunctionArgumentList),
}
impl fmt::Display for FunctionArguments {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FunctionArguments::None => Ok(()),
FunctionArguments::Subquery(query) => write!(f, "({})", query),
FunctionArguments::List(args) => write!(f, "({})", args),
}
}
}
/// This represents everything inside the parentheses when calling a function.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct FunctionArgumentList {
/// `[ ALL | DISTINCT ]
pub duplicate_treatment: Option<DuplicateTreatment>,
/// The function arguments.
pub args: Vec<FunctionArg>,
/// Additional clauses specified within the argument list.
pub clauses: Vec<FunctionArgumentClause>,
}
impl fmt::Display for FunctionArgumentList {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(duplicate_treatment) = self.duplicate_treatment {
write!(f, "{} ", duplicate_treatment)?;
}
write!(f, "{}", display_comma_separated(&self.args))?;
if !self.clauses.is_empty() {
write!(f, " {}", display_separated(&self.clauses, " "))?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FunctionArgumentClause {
/// Indicates how `NULL`s should be handled in the calculation, e.g. in `FIRST_VALUE` on [BigQuery].
///
/// Syntax:
/// ```plaintext
/// { IGNORE | RESPECT } NULLS ]
/// ```
///
/// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/navigation_functions#first_value
IgnoreOrRespectNulls(NullTreatment),
/// 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
OrderBy(Vec<OrderByExpr>),
/// Specifies a limit for the `ARRAY_AGG` and `ARRAY_CONCAT_AGG` functions on BigQuery.
Limit(Expr),
/// Specifies the behavior on overflow of the `LISTAGG` function.
///
/// See <https://trino.io/docs/current/functions/aggregate.html>.
OnOverflow(ListAggOnOverflow),
}
impl fmt::Display for FunctionArgumentClause {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FunctionArgumentClause::IgnoreOrRespectNulls(null_treatment) => {
write!(f, "{}", null_treatment)
}
FunctionArgumentClause::OrderBy(order_by) => {
write!(f, "ORDER BY {}", display_comma_separated(order_by))
}
FunctionArgumentClause::Limit(limit) => write!(f, "LIMIT {limit}"),
FunctionArgumentClause::OnOverflow(on_overflow) => write!(f, "{on_overflow}"),
}
}
}
#[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 DuplicateTreatment {
/// Perform the calculation only unique values.
Distinct,
/// Retain all duplicate values (the default).
All,
}
impl fmt::Display for DuplicateTreatment {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DuplicateTreatment::Distinct => write!(f, "DISTINCT"),
DuplicateTreatment::All => write!(f, "ALL"),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
@ -4866,48 +4963,6 @@ impl fmt::Display for AnalyzeFormat {
}
}
impl fmt::Display for Function {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.special {
write!(f, "{}", self.name)?;
} else {
let order_by = if !self.order_by.is_empty() {
" ORDER BY "
} else {
""
};
write!(
f,
"{}({}{}{order_by}{}{})",
self.name,
if self.distinct { "DISTINCT " } else { "" },
display_comma_separated(&self.args),
display_comma_separated(&self.order_by),
match self.null_treatment {
Some(NullTreatmentType::FunctionArg(null_treatment)) => {
Cow::from(format!(" {null_treatment}"))
}
_ => Cow::from(""),
}
)?;
if let Some(filter_cond) = &self.filter {
write!(f, " FILTER (WHERE {filter_cond})")?;
}
if let Some(NullTreatmentType::AfterFunction(null_treatment)) = &self.null_treatment {
write!(f, " {null_treatment}")?;
}
if let Some(o) = &self.over {
write!(f, " OVER {o}")?;
}
}
Ok(())
}
}
/// External table's available file format
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@ -4937,45 +4992,6 @@ impl fmt::Display for FileFormat {
}
}
/// A `LISTAGG` invocation `LISTAGG( [ DISTINCT ] <expr>[, <separator> ] [ON OVERFLOW <on_overflow>] ) )
/// [ WITHIN GROUP (ORDER BY <within_group1>[, ...] ) ]`
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct ListAgg {
pub distinct: bool,
pub expr: Box<Expr>,
pub separator: Option<Box<Expr>>,
pub on_overflow: Option<ListAggOnOverflow>,
pub within_group: Vec<OrderByExpr>,
}
impl fmt::Display for ListAgg {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"LISTAGG({}{}",
if self.distinct { "DISTINCT " } else { "" },
self.expr
)?;
if let Some(separator) = &self.separator {
write!(f, ", {separator}")?;
}
if let Some(on_overflow) = &self.on_overflow {
write!(f, "{on_overflow}")?;
}
write!(f, ")")?;
if !self.within_group.is_empty() {
write!(
f,
" WITHIN GROUP (ORDER BY {})",
display_comma_separated(&self.within_group)
)?;
}
Ok(())
}
}
/// The `ON OVERFLOW` clause of a LISTAGG invocation
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@ -4993,7 +5009,7 @@ pub enum ListAggOnOverflow {
impl fmt::Display for ListAggOnOverflow {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, " ON OVERFLOW")?;
write!(f, "ON OVERFLOW")?;
match self {
ListAggOnOverflow::Error => write!(f, " ERROR"),
ListAggOnOverflow::Truncate { filler, with_count } => {
@ -5012,50 +5028,6 @@ impl fmt::Display for ListAggOnOverflow {
}
}
/// An `ARRAY_AGG` invocation `ARRAY_AGG( [ DISTINCT ] <expr> [ORDER BY <expr>] [LIMIT <n>] )`
/// Or `ARRAY_AGG( [ DISTINCT ] <expr> ) [ WITHIN GROUP ( ORDER BY <expr> ) ]`
/// ORDER BY position is defined differently for BigQuery, Postgres and Snowflake.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct ArrayAgg {
pub distinct: bool,
pub expr: Box<Expr>,
pub order_by: Option<Vec<OrderByExpr>>,
pub limit: Option<Box<Expr>>,
pub within_group: bool, // order by is used inside a within group or not
}
impl fmt::Display for ArrayAgg {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"ARRAY_AGG({}{}",
if self.distinct { "DISTINCT " } else { "" },
self.expr
)?;
if !self.within_group {
if let Some(order_by) = &self.order_by {
write!(f, " ORDER BY {}", display_comma_separated(order_by))?;
}
if let Some(limit) = &self.limit {
write!(f, " LIMIT {limit}")?;
}
}
write!(f, ")")?;
if self.within_group {
if let Some(order_by) = &self.order_by {
write!(
f,
" WITHIN GROUP (ORDER BY {})",
display_comma_separated(order_by)
)?;
}
}
Ok(())
}
}
#[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

@ -515,7 +515,7 @@ where
/// ```
/// # use sqlparser::parser::Parser;
/// # use sqlparser::dialect::GenericDialect;
/// # use sqlparser::ast::{Expr, Function, FunctionArg, FunctionArgExpr, Ident, ObjectName, Value, visit_expressions_mut, visit_statements_mut};
/// # use sqlparser::ast::*;
/// # use core::ops::ControlFlow;
/// let sql = "SELECT x, y FROM t";
/// let mut statements = Parser::parse_sql(&GenericDialect{}, sql).unwrap();
@ -525,9 +525,15 @@ where
/// let old_expr = std::mem::replace(expr, Expr::Value(Value::Null));
/// *expr = Expr::Function(Function {
/// name: ObjectName(vec![Ident::new("f")]),
/// args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(old_expr))],
/// args: FunctionArguments::List(FunctionArgumentList {
/// duplicate_treatment: None,
/// args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(old_expr))],
/// clauses: vec![],
/// }),
/// null_treatment: None,
/// filter: None, over: None, distinct: false, special: false, order_by: vec![],
/// filter: None,
/// over: None,
/// within_group: vec![],
/// });
/// }
/// ControlFlow::<()>::Continue(())

View file

@ -87,7 +87,6 @@ define_keywords!(
ARCHIVE,
ARE,
ARRAY,
ARRAY_AGG,
ARRAY_MAX_CARDINALITY,
AS,
ASC,
@ -401,7 +400,6 @@ define_keywords!(
LIKE_REGEX,
LIMIT,
LINES,
LISTAGG,
LN,
LOAD,
LOCAL,

View file

@ -208,13 +208,6 @@ impl From<bool> for MatchedTrailingBracket {
}
}
/// Output of the [`Parser::parse_window_function_args`] function.
struct ParseWindowFunctionArgsOutput {
args: Vec<FunctionArg>,
order_by: Vec<OrderByExpr>,
null_treatment: Option<NullTreatment>,
}
/// Options that control how the [`Parser`] parses SQL text
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParserOptions {
@ -1006,13 +999,11 @@ impl<'a> Parser<'a> {
{
Ok(Expr::Function(Function {
name: ObjectName(vec![w.to_ident()]),
args: vec![],
args: FunctionArguments::None,
null_treatment: None,
filter: None,
over: None,
distinct: false,
special: true,
order_by: vec![],
within_group: vec![],
}))
}
Keyword::CURRENT_TIMESTAMP
@ -1038,7 +1029,6 @@ impl<'a> Parser<'a> {
Keyword::OVERLAY => self.parse_overlay_expr(),
Keyword::TRIM => self.parse_trim_expr(),
Keyword::INTERVAL => self.parse_interval(),
Keyword::LISTAGG => self.parse_listagg_expr(),
// Treat ARRAY[1,2,3] as an array [1,2,3], otherwise try as subquery or a function call
Keyword::ARRAY if self.peek_token() == Token::LBracket => {
self.expect_token(&Token::LBracket)?;
@ -1049,9 +1039,17 @@ impl<'a> Parser<'a> {
&& !dialect_of!(self is ClickHouseDialect) =>
{
self.expect_token(&Token::LParen)?;
self.parse_array_subquery()
let query = self.parse_boxed_query()?;
self.expect_token(&Token::RParen)?;
Ok(Expr::Function(Function {
name: ObjectName(vec![w.to_ident()]),
args: FunctionArguments::Subquery(query),
filter: None,
null_treatment: None,
over: None,
within_group: vec![],
}))
}
Keyword::ARRAY_AGG => self.parse_array_agg_expr(),
Keyword::NOT => self.parse_not(),
Keyword::MATCH if dialect_of!(self is MySqlDialect | GenericDialect) => {
self.parse_match_against()
@ -1235,12 +1233,39 @@ impl<'a> Parser<'a> {
pub fn parse_function(&mut self, name: ObjectName) -> Result<Expr, ParserError> {
self.expect_token(&Token::LParen)?;
let distinct = self.parse_all_or_distinct()?.is_some();
let ParseWindowFunctionArgsOutput {
args,
order_by,
null_treatment,
} = self.parse_window_function_args()?;
// Snowflake permits a subquery to be passed as an argument without
// an enclosing set of parens if it's the only argument.
if dialect_of!(self is SnowflakeDialect)
&& self
.parse_one_of_keywords(&[Keyword::WITH, Keyword::SELECT])
.is_some()
{
self.prev_token();
let subquery = self.parse_boxed_query()?;
self.expect_token(&Token::RParen)?;
return Ok(Expr::Function(Function {
name,
args: FunctionArguments::Subquery(subquery),
filter: None,
null_treatment: None,
over: None,
within_group: vec![],
}));
}
let args = self.parse_function_argument_list()?;
let within_group = if self.parse_keywords(&[Keyword::WITHIN, Keyword::GROUP]) {
self.expect_token(&Token::LParen)?;
self.expect_keywords(&[Keyword::ORDER, Keyword::BY])?;
let order_by = self.parse_comma_separated(Parser::parse_order_by_expr)?;
self.expect_token(&Token::RParen)?;
order_by
} else {
vec![]
};
let filter = if self.dialect.supports_filter_during_aggregation()
&& self.parse_keyword(Keyword::FILTER)
&& self.consume_token(&Token::LParen)
@ -1255,12 +1280,16 @@ impl<'a> Parser<'a> {
// Syntax for null treatment shows up either in the args list
// or after the function call, but not both.
let mut null_treatment = null_treatment.map(NullTreatmentType::FunctionArg);
if null_treatment.is_none() {
null_treatment = self
.parse_null_treatment()?
.map(NullTreatmentType::AfterFunction);
}
let null_treatment = if args
.clauses
.iter()
.all(|clause| !matches!(clause, FunctionArgumentClause::IgnoreOrRespectNulls(_)))
{
self.parse_null_treatment()?
} else {
None
};
let over = if self.parse_keyword(Keyword::OVER) {
if self.consume_token(&Token::LParen) {
let window_spec = self.parse_window_spec()?;
@ -1271,15 +1300,14 @@ impl<'a> Parser<'a> {
} else {
None
};
Ok(Expr::Function(Function {
name,
args,
args: FunctionArguments::List(args),
null_treatment,
filter,
over,
distinct,
special: false,
order_by,
within_group,
}))
}
@ -1300,25 +1328,18 @@ impl<'a> Parser<'a> {
}
pub fn parse_time_functions(&mut self, name: ObjectName) -> Result<Expr, ParserError> {
let (args, order_by, null_treatment, special) = if self.consume_token(&Token::LParen) {
let ParseWindowFunctionArgsOutput {
args,
order_by,
null_treatment,
} = self.parse_window_function_args()?;
(args, order_by, null_treatment, false)
let args = if self.consume_token(&Token::LParen) {
FunctionArguments::List(self.parse_function_argument_list()?)
} else {
(vec![], vec![], None, true)
FunctionArguments::None
};
Ok(Expr::Function(Function {
name,
args,
null_treatment: null_treatment.map(NullTreatmentType::FunctionArg),
filter: None,
over: None,
distinct: false,
special,
order_by,
null_treatment: None,
within_group: vec![],
}))
}
@ -1750,28 +1771,10 @@ impl<'a> Parser<'a> {
}
}
// Parses an array constructed from a subquery
pub fn parse_array_subquery(&mut self) -> Result<Expr, ParserError> {
let query = self.parse_boxed_query()?;
self.expect_token(&Token::RParen)?;
Ok(Expr::ArraySubquery(query))
}
/// Parse a SQL LISTAGG expression, e.g. `LISTAGG(...) WITHIN GROUP (ORDER BY ...)`.
pub fn parse_listagg_expr(&mut self) -> Result<Expr, ParserError> {
self.expect_token(&Token::LParen)?;
let distinct = self.parse_all_or_distinct()?.is_some();
let expr = Box::new(self.parse_expr()?);
// While ANSI SQL would would require the separator, Redshift makes this optional. Here we
// choose to make the separator optional as this provides the more general implementation.
let separator = if self.consume_token(&Token::Comma) {
Some(Box::new(self.parse_expr()?))
} else {
None
};
let on_overflow = if self.parse_keywords(&[Keyword::ON, Keyword::OVERFLOW]) {
pub fn parse_listagg_on_overflow(&mut self) -> Result<Option<ListAggOnOverflow>, ParserError> {
if self.parse_keywords(&[Keyword::ON, Keyword::OVERFLOW]) {
if self.parse_keyword(Keyword::ERROR) {
Some(ListAggOnOverflow::Error)
Ok(Some(ListAggOnOverflow::Error))
} else {
self.expect_keyword(Keyword::TRUNCATE)?;
let filler = match self.peek_token().token {
@ -1794,80 +1797,11 @@ impl<'a> Parser<'a> {
self.expected("either WITH or WITHOUT in LISTAGG", self.peek_token())?;
}
self.expect_keyword(Keyword::COUNT)?;
Some(ListAggOnOverflow::Truncate { filler, with_count })
Ok(Some(ListAggOnOverflow::Truncate { filler, with_count }))
}
} else {
None
};
self.expect_token(&Token::RParen)?;
// Once again ANSI SQL requires WITHIN GROUP, but Redshift does not. Again we choose the
// more general implementation.
let within_group = if self.parse_keywords(&[Keyword::WITHIN, Keyword::GROUP]) {
self.expect_token(&Token::LParen)?;
self.expect_keywords(&[Keyword::ORDER, Keyword::BY])?;
let order_by_expr = self.parse_comma_separated(Parser::parse_order_by_expr)?;
self.expect_token(&Token::RParen)?;
order_by_expr
} else {
vec![]
};
Ok(Expr::ListAgg(ListAgg {
distinct,
expr,
separator,
on_overflow,
within_group,
}))
}
pub fn parse_array_agg_expr(&mut self) -> Result<Expr, ParserError> {
self.expect_token(&Token::LParen)?;
let distinct = self.parse_keyword(Keyword::DISTINCT);
let expr = Box::new(self.parse_expr()?);
// ANSI SQL and BigQuery define ORDER BY inside function.
if !self.dialect.supports_within_after_array_aggregation() {
let order_by = if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) {
Some(self.parse_comma_separated(Parser::parse_order_by_expr)?)
} else {
None
};
let limit = if self.parse_keyword(Keyword::LIMIT) {
self.parse_limit()?.map(Box::new)
} else {
None
};
self.expect_token(&Token::RParen)?;
return Ok(Expr::ArrayAgg(ArrayAgg {
distinct,
expr,
order_by,
limit,
within_group: false,
}));
Ok(None)
}
// Snowflake defines ORDER BY in within group instead of inside the function like
// ANSI SQL.
self.expect_token(&Token::RParen)?;
let within_group = if self.parse_keywords(&[Keyword::WITHIN, Keyword::GROUP]) {
self.expect_token(&Token::LParen)?;
let order_by = if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) {
Some(self.parse_comma_separated(Parser::parse_order_by_expr)?)
} else {
None
};
self.expect_token(&Token::RParen)?;
order_by
} else {
None
};
Ok(Expr::ArrayAgg(ArrayAgg {
distinct,
expr,
order_by: within_group,
limit: None,
within_group: true,
}))
}
// This function parses date/time fields for the EXTRACT function-like
@ -6131,13 +6065,11 @@ impl<'a> Parser<'a> {
} else {
Ok(Statement::Call(Function {
name: object_name,
args: vec![],
args: FunctionArguments::None,
over: None,
distinct: false,
filter: None,
null_treatment: None,
special: true,
order_by: vec![],
within_group: vec![],
}))
}
}
@ -9466,52 +9398,58 @@ impl<'a> Parser<'a> {
/// FIRST_VALUE(x ORDER BY 1,2,3);
/// FIRST_VALUE(x IGNORE NULL);
/// ```
fn parse_window_function_args(&mut self) -> Result<ParseWindowFunctionArgsOutput, ParserError> {
fn parse_function_argument_list(&mut self) -> Result<FunctionArgumentList, ParserError> {
if self.consume_token(&Token::RParen) {
Ok(ParseWindowFunctionArgsOutput {
return Ok(FunctionArgumentList {
duplicate_treatment: None,
args: vec![],
order_by: vec![],
null_treatment: None,
})
} else {
// Snowflake permits a subquery to be passed as an argument without
// an enclosing set of parens if it's the only argument.
if dialect_of!(self is SnowflakeDialect)
&& self
.parse_one_of_keywords(&[Keyword::WITH, Keyword::SELECT])
.is_some()
{
self.prev_token();
let subquery = self.parse_boxed_query()?;
self.expect_token(&Token::RParen)?;
return Ok(ParseWindowFunctionArgsOutput {
args: vec![FunctionArg::Unnamed(FunctionArgExpr::from(Expr::Subquery(
subquery,
)))],
order_by: vec![],
null_treatment: None,
});
clauses: vec![],
});
}
let duplicate_treatment = self.parse_duplicate_treatment()?;
let args = self.parse_comma_separated(Parser::parse_function_args)?;
let mut clauses = vec![];
if self.dialect.supports_window_function_null_treatment_arg() {
if let Some(null_treatment) = self.parse_null_treatment()? {
clauses.push(FunctionArgumentClause::IgnoreOrRespectNulls(null_treatment));
}
}
let args = self.parse_comma_separated(Parser::parse_function_args)?;
let order_by = if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) {
self.parse_comma_separated(Parser::parse_order_by_expr)?
} else {
Default::default()
};
if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) {
clauses.push(FunctionArgumentClause::OrderBy(
self.parse_comma_separated(Parser::parse_order_by_expr)?,
));
}
let null_treatment = if self.dialect.supports_window_function_null_treatment_arg() {
self.parse_null_treatment()?
} else {
None
};
if self.parse_keyword(Keyword::LIMIT) {
clauses.push(FunctionArgumentClause::Limit(self.parse_expr()?));
}
self.expect_token(&Token::RParen)?;
Ok(ParseWindowFunctionArgsOutput {
args,
order_by,
null_treatment,
})
if let Some(on_overflow) = self.parse_listagg_on_overflow()? {
clauses.push(FunctionArgumentClause::OnOverflow(on_overflow));
}
self.expect_token(&Token::RParen)?;
Ok(FunctionArgumentList {
duplicate_treatment,
args,
clauses,
})
}
fn parse_duplicate_treatment(&mut self) -> Result<Option<DuplicateTreatment>, ParserError> {
let loc = self.peek_token().location;
match (
self.parse_keyword(Keyword::ALL),
self.parse_keyword(Keyword::DISTINCT),
) {
(true, false) => Ok(Some(DuplicateTreatment::All)),
(false, true) => Ok(Some(DuplicateTreatment::Distinct)),
(false, false) => Ok(None),
(true, true) => parser_err!("Cannot specify both ALL and DISTINCT".to_string(), loc),
}
}
@ -9525,31 +9463,12 @@ impl<'a> Parser<'a> {
Expr::Wildcard => Ok(SelectItem::Wildcard(
self.parse_wildcard_additional_options()?,
)),
expr => {
let expr: Expr = if self.dialect.supports_filter_during_aggregation()
&& self.parse_keyword(Keyword::FILTER)
{
let i = self.index - 1;
if self.consume_token(&Token::LParen) && self.parse_keyword(Keyword::WHERE) {
let filter = self.parse_expr()?;
self.expect_token(&Token::RParen)?;
Expr::AggregateExpressionWithFilter {
expr: Box::new(expr),
filter: Box::new(filter),
}
} else {
self.index = i;
expr
}
} else {
expr
};
self.parse_optional_alias(keywords::RESERVED_FOR_COLUMN_ALIAS)
.map(|alias| match alias {
Some(alias) => SelectItem::ExprWithAlias { expr, alias },
None => SelectItem::UnnamedExpr(expr),
})
}
expr => self
.parse_optional_alias(keywords::RESERVED_FOR_COLUMN_ALIAS)
.map(|alias| match alias {
Some(alias) => SelectItem::ExprWithAlias { expr, alias },
None => SelectItem::UnnamedExpr(expr),
}),
}
}

View file

@ -322,16 +322,17 @@ pub fn join(relation: TableFactor) -> Join {
pub fn call(function: &str, args: impl IntoIterator<Item = Expr>) -> Expr {
Expr::Function(Function {
name: ObjectName(vec![Ident::new(function)]),
args: args
.into_iter()
.map(FunctionArgExpr::Expr)
.map(FunctionArg::Unnamed)
.collect(),
args: FunctionArguments::List(FunctionArgumentList {
duplicate_treatment: None,
args: args
.into_iter()
.map(|arg| FunctionArg::Unnamed(FunctionArgExpr::Expr(arg)))
.collect(),
clauses: vec![],
}),
filter: None,
null_treatment: None,
over: None,
distinct: false,
special: false,
order_by: vec![],
within_group: vec![],
})
}