diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 705348548..e19a12b5e 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -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, - filter: Box, - }, /// `CASE [] WHEN THEN ... [ELSE ] END` /// /// Note we only recognize a complete single expression as ``, @@ -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), - /// An array subquery constructor, e.g. `SELECT ARRAY(SELECT 1 UNION SELECT 2)` - ArraySubquery(Box), - /// 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>), /// 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, + /// 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>, - /// Specifies Ignore / Respect NULL within window functions. + /// Indicates how `NULL`s should be handled in the calculation. + /// + /// Example: + /// ```plaintext + /// FIRST_VALUE( ) [ { 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, + pub null_treatment: Option, + /// The `OVER` clause, indicating a window function call. pub over: Option, - /// 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, + /// A clause used with certain aggregate functions to control the ordering + /// within grouped sets before the function is applied. + /// + /// Syntax: + /// ```plaintext + /// (expression) WITHIN GROUP (ORDER BY key [ASC | DESC], ...) + /// ``` + pub within_group: Vec, +} + +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), + /// 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, + /// The function arguments. + pub args: Vec, + /// Additional clauses specified within the argument list. + pub clauses: Vec, +} + +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), + /// 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 . + 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 ] [, ] [ON OVERFLOW ] ) ) -/// [ WITHIN GROUP (ORDER BY [, ...] ) ]` -#[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, - pub separator: Option>, - pub on_overflow: Option, - pub within_group: Vec, -} - -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 ] [ORDER BY ] [LIMIT ] )` -/// Or `ARRAY_AGG( [ DISTINCT ] ) [ WITHIN GROUP ( ORDER BY ) ]` -/// 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, - pub order_by: Option>, - pub limit: Option>, - 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))] diff --git a/src/ast/visitor.rs b/src/ast/visitor.rs index 459fd8214..57dcca2e5 100644 --- a/src/ast/visitor.rs +++ b/src/ast/visitor.rs @@ -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(()) diff --git a/src/keywords.rs b/src/keywords.rs index 6381d71bf..73d388cfb 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -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, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 360cabf13..5f6f28088 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -208,13 +208,6 @@ impl From for MatchedTrailingBracket { } } -/// Output of the [`Parser::parse_window_function_args`] function. -struct ParseWindowFunctionArgsOutput { - args: Vec, - order_by: Vec, - null_treatment: Option, -} - /// 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 { 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 { - 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 { - 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 { - 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, 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 { - 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 { + fn parse_function_argument_list(&mut self) -> Result { 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, 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), + }), } } diff --git a/src/test_utils.rs b/src/test_utils.rs index 23a53d6b8..464366ae4 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -322,16 +322,17 @@ pub fn join(relation: TableFactor) -> Join { pub fn call(function: &str, args: impl IntoIterator) -> 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![], }) } diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs index bac973ed4..b2a0f9090 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -1824,18 +1824,7 @@ fn parse_map_access_expr() { MapAccessSyntax::Bracket, ), map_access_key( - Expr::Function(Function { - name: ObjectName(vec![Ident::new("safe_offset")]), - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( - number("2"), - )))], - filter: None, - null_treatment: None, - over: None, - distinct: false, - special: false, - order_by: vec![], - }), + call("safe_offset", [Expr::Value(number("2"))]), MapAccessSyntax::Bracket, ), map_access_key( @@ -1903,3 +1892,14 @@ fn test_select_as_value() { 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); } + +#[test] +fn test_array_agg() { + bigquery_and_generic().verified_expr("ARRAY_AGG(state)"); + bigquery_and_generic().verified_expr("ARRAY_CONCAT_AGG(x LIMIT 2)"); + bigquery_and_generic().verified_expr("ARRAY_AGG(state IGNORE NULLS LIMIT 10)"); + bigquery_and_generic().verified_expr("ARRAY_AGG(state RESPECT NULLS ORDER BY population)"); + bigquery_and_generic() + .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))"); +} diff --git a/tests/sqlparser_clickhouse.rs b/tests/sqlparser_clickhouse.rs index 0b49f0070..7150a9489 100644 --- a/tests/sqlparser_clickhouse.rs +++ b/tests/sqlparser_clickhouse.rs @@ -40,23 +40,13 @@ fn parse_map_access_expr() { quote_style: None, })), keys: vec![MapAccessKey { - key: Expr::Function(Function { - name: ObjectName(vec!["indexOf".into()]), - args: vec![ - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Identifier( - Ident::new("string_names") - ))), - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( - Value::SingleQuotedString("endpoint".to_string()) - ))), - ], - null_treatment: None, - filter: None, - over: None, - distinct: false, - special: false, - order_by: vec![], - }), + key: call( + "indexOf", + [ + Expr::Identifier(Ident::new("string_names")), + Expr::Value(Value::SingleQuotedString("endpoint".to_string())) + ] + ), syntax: MapAccessSyntax::Bracket }], })], @@ -84,23 +74,13 @@ fn parse_map_access_expr() { left: Box::new(MapAccess { column: Box::new(Identifier(Ident::new("string_value"))), keys: vec![MapAccessKey { - key: Expr::Function(Function { - name: ObjectName(vec![Ident::new("indexOf")]), - args: vec![ - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Identifier( - Ident::new("string_name") - ))), - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( - Value::SingleQuotedString("app".to_string()) - ))), - ], - null_treatment: None, - filter: None, - over: None, - distinct: false, - special: false, - order_by: vec![], - }), + key: call( + "indexOf", + [ + Expr::Identifier(Ident::new("string_name")), + Expr::Value(Value::SingleQuotedString("app".to_string())) + ] + ), syntax: MapAccessSyntax::Bracket }], }), @@ -144,19 +124,13 @@ fn parse_array_fn() { let sql = "SELECT array(x1, x2) FROM foo"; let select = clickhouse().verified_only_select(sql); assert_eq!( - &Expr::Function(Function { - name: ObjectName(vec![Ident::new("array")]), - args: vec![ - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Identifier(Ident::new("x1")))), - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Identifier(Ident::new("x2")))), - ], - null_treatment: None, - filter: None, - over: None, - distinct: false, - special: false, - order_by: vec![], - }), + &call( + "array", + [ + Expr::Identifier(Ident::new("x1")), + Expr::Identifier(Ident::new("x2")) + ] + ), expr_from_projection(only(&select.projection)) ); } @@ -209,13 +183,15 @@ fn parse_delimited_identifiers() { assert_eq!( &Expr::Function(Function { name: ObjectName(vec![Ident::with_quote('"', "myfun")]), - args: vec![], + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![], + clauses: vec![], + }), null_treatment: None, filter: None, over: None, - distinct: false, - special: false, - order_by: vec![], + within_group: vec![], }), expr_from_projection(&select.projection[1]), ); diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index ae281296d..9baf380a0 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -1042,13 +1042,15 @@ fn parse_select_count_wildcard() { assert_eq!( &Expr::Function(Function { name: ObjectName(vec![Ident::new("COUNT")]), - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Wildcard)], + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Wildcard)], + clauses: vec![], + }), null_treatment: None, filter: None, over: None, - distinct: false, - special: false, - order_by: vec![], + within_group: vec![] }), expr_from_projection(only(&select.projection)) ); @@ -1061,24 +1063,24 @@ fn parse_select_count_distinct() { assert_eq!( &Expr::Function(Function { name: ObjectName(vec![Ident::new("COUNT")]), - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::UnaryOp { - op: UnaryOperator::Plus, - expr: Box::new(Expr::Identifier(Ident::new("x"))), - }))], + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: Some(DuplicateTreatment::Distinct), + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::UnaryOp { + op: UnaryOperator::Plus, + expr: Box::new(Expr::Identifier(Ident::new("x"))), + }))], + clauses: vec![], + }), null_treatment: None, + within_group: vec![], filter: None, - over: None, - distinct: true, - special: false, - order_by: vec![], + over: None }), expr_from_projection(only(&select.projection)) ); - one_statement_parses_to( - "SELECT COUNT(ALL +x) FROM customer", - "SELECT COUNT(+x) FROM customer", - ); + verified_stmt("SELECT COUNT(ALL +x) FROM customer"); + verified_stmt("SELECT COUNT(+x) FROM customer"); let sql = "SELECT COUNT(ALL DISTINCT + x) FROM customer"; let res = parse_sql_statements(sql); @@ -2142,13 +2144,15 @@ fn parse_select_having() { Some(Expr::BinaryOp { left: Box::new(Expr::Function(Function { name: ObjectName(vec![Ident::new("COUNT")]), - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Wildcard)], + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Wildcard)], + clauses: vec![], + }), null_treatment: None, filter: None, over: None, - distinct: false, - special: false, - order_by: vec![], + within_group: vec![] })), op: BinaryOperator::Gt, right: Box::new(Expr::Value(number("1"))), @@ -2169,7 +2173,11 @@ fn parse_select_qualify() { Some(Expr::BinaryOp { left: Box::new(Expr::Function(Function { name: ObjectName(vec![Ident::new("ROW_NUMBER")]), - args: vec![], + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![], + clauses: vec![], + }), null_treatment: None, filter: None, over: Some(WindowType::WindowSpec(WindowSpec { @@ -2182,9 +2190,7 @@ fn parse_select_qualify() { }], window_frame: None, })), - distinct: false, - special: false, - order_by: vec![], + within_group: vec![] })), op: BinaryOperator::Eq, right: Box::new(Expr::Value(number("1"))), @@ -2502,9 +2508,57 @@ fn parse_floor_datetime() { #[test] fn parse_listagg() { - let sql = "SELECT LISTAGG(DISTINCT dateid, ', ' ON OVERFLOW TRUNCATE '%' WITHOUT COUNT) \ - WITHIN GROUP (ORDER BY id, username)"; - let select = verified_only_select(sql); + let select = verified_only_select(concat!( + "SELECT LISTAGG(DISTINCT dateid, ', ' ON OVERFLOW TRUNCATE '%' WITHOUT COUNT) ", + "WITHIN GROUP (ORDER BY id, username)", + )); + + assert_eq!( + &Expr::Function(Function { + name: ObjectName(vec![Ident::new("LISTAGG")]), + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: Some(DuplicateTreatment::Distinct), + args: vec![ + FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Identifier(Ident::new( + "dateid" + )))), + FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( + Value::SingleQuotedString(", ".to_owned()) + ))) + ], + clauses: vec![FunctionArgumentClause::OnOverflow( + ListAggOnOverflow::Truncate { + filler: Some(Box::new(Expr::Value(Value::SingleQuotedString( + "%".to_string(), + )))), + with_count: false, + } + )], + }), + filter: None, + null_treatment: None, + over: None, + within_group: vec![ + OrderByExpr { + expr: Expr::Identifier(Ident { + value: "id".to_string(), + quote_style: None, + }), + asc: None, + nulls_first: None, + }, + OrderByExpr { + expr: Expr::Identifier(Ident { + value: "username".to_string(), + quote_style: None, + }), + asc: None, + nulls_first: None, + }, + ] + }), + expr_from_projection(only(&select.projection)) + ); verified_stmt("SELECT LISTAGG(sellerid) WITHIN GROUP (ORDER BY dateid)"); verified_stmt("SELECT LISTAGG(dateid)"); @@ -2512,44 +2566,6 @@ fn parse_listagg() { verified_stmt("SELECT LISTAGG(dateid ON OVERFLOW ERROR)"); verified_stmt("SELECT LISTAGG(dateid ON OVERFLOW TRUNCATE N'...' WITH COUNT)"); verified_stmt("SELECT LISTAGG(dateid ON OVERFLOW TRUNCATE X'deadbeef' WITH COUNT)"); - - let expr = Box::new(Expr::Identifier(Ident::new("dateid"))); - let on_overflow = Some(ListAggOnOverflow::Truncate { - filler: Some(Box::new(Expr::Value(Value::SingleQuotedString( - "%".to_string(), - )))), - with_count: false, - }); - let within_group = vec![ - OrderByExpr { - expr: Expr::Identifier(Ident { - value: "id".to_string(), - quote_style: None, - }), - asc: None, - nulls_first: None, - }, - OrderByExpr { - expr: Expr::Identifier(Ident { - value: "username".to_string(), - quote_style: None, - }), - asc: None, - nulls_first: None, - }, - ]; - assert_eq!( - &Expr::ListAgg(ListAgg { - distinct: true, - expr, - separator: Some(Box::new(Expr::Value(Value::SingleQuotedString( - ", ".to_string() - )))), - on_overflow, - within_group, - }), - expr_from_projection(only(&select.projection)) - ); } #[test] @@ -2575,12 +2591,6 @@ fn parse_array_agg_func() { ] { supported_dialects.verified_stmt(sql); } - - // follows special-case array_agg code path. fails in everything except postgres - let wc_sql = "SELECT ARRAY_AGG(sections_tbl.*) AS sections FROM sections_tbl"; - all_dialects_but_pg() - .parse_sql_statements(wc_sql) - .expect_err("should have failed"); } #[test] @@ -2664,24 +2674,31 @@ fn parse_window_function_null_treatment_arg() { unreachable!() }; assert_eq!(ObjectName(vec![Ident::new("FIRST_VALUE")]), actual.name); - assert!(actual.order_by.is_empty()); - assert_eq!(1, actual.args.len()); + let FunctionArguments::List(arg_list) = &actual.args else { + panic!("expected argument list") + }; + assert!({ + arg_list + .clauses + .iter() + .all(|clause| !matches!(clause, FunctionArgumentClause::OrderBy(_))) + }); + assert_eq!(1, arg_list.args.len()); let FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Identifier(actual_expr))) = - &actual.args[0] + &arg_list.args[0] else { unreachable!() }; assert_eq!(&Ident::new(expected_expr), actual_expr); - let Some(NullTreatmentType::FunctionArg(actual_null_treatment)) = actual.null_treatment - else { - unreachable!() - }; - assert_eq!(expected_null_treatment, actual_null_treatment); + assert_eq!( + Some(expected_null_treatment), + arg_list.clauses.iter().find_map(|clause| match clause { + FunctionArgumentClause::IgnoreOrRespectNulls(nt) => Some(*nt), + _ => None, + }) + ); } - let sql = "SELECT FIRST_VALUE(a ORDER BY b IGNORE NULLS) OVER () FROM t1"; - dialects.verified_stmt(sql); - let sql = "SELECT LAG(1 IGNORE NULLS) IGNORE NULLS OVER () FROM t1"; assert_eq!( dialects.parse_sql_statements(sql).unwrap_err(), @@ -4074,18 +4091,7 @@ fn parse_scalar_function_in_projection() { let sql = dbg!(format!("SELECT {function_name}(id) FROM foo")); let select = verified_only_select(&sql); assert_eq!( - &Expr::Function(Function { - name: ObjectName(vec![Ident::new(function_name)]), - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( - Expr::Identifier(Ident::new("id")) - ))], - null_treatment: None, - filter: None, - over: None, - distinct: false, - special: false, - order_by: vec![], - }), + &call(function_name, [Expr::Identifier(Ident::new("id"))]), expr_from_projection(only(&select.projection)) ); } @@ -4199,28 +4205,30 @@ fn parse_named_argument_function() { assert_eq!( &Expr::Function(Function { name: ObjectName(vec![Ident::new("FUN")]), - args: vec![ - FunctionArg::Named { - name: Ident::new("a"), - arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString( - "1".to_owned() - ))), - operator: FunctionArgOperator::RightArrow - }, - FunctionArg::Named { - name: Ident::new("b"), - arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString( - "2".to_owned() - ))), - operator: FunctionArgOperator::RightArrow - }, - ], + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![ + FunctionArg::Named { + name: Ident::new("a"), + arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString( + "1".to_owned() + ))), + operator: FunctionArgOperator::RightArrow + }, + FunctionArg::Named { + name: Ident::new("b"), + arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString( + "2".to_owned() + ))), + operator: FunctionArgOperator::RightArrow + }, + ], + clauses: vec![], + }), null_treatment: None, filter: None, over: None, - distinct: false, - special: false, - order_by: vec![], + within_group: vec![] }), expr_from_projection(only(&select.projection)) ); @@ -4235,28 +4243,30 @@ fn parse_named_argument_function_with_eq_operator() { assert_eq!( &Expr::Function(Function { name: ObjectName(vec![Ident::new("FUN")]), - args: vec![ - FunctionArg::Named { - name: Ident::new("a"), - arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString( - "1".to_owned() - ))), - operator: FunctionArgOperator::Equals - }, - FunctionArg::Named { - name: Ident::new("b"), - arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString( - "2".to_owned() - ))), - operator: FunctionArgOperator::Equals - }, - ], + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![ + FunctionArg::Named { + name: Ident::new("a"), + arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString( + "1".to_owned() + ))), + operator: FunctionArgOperator::Equals + }, + FunctionArg::Named { + name: Ident::new("b"), + arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString( + "2".to_owned() + ))), + operator: FunctionArgOperator::Equals + }, + ], + clauses: vec![], + }), null_treatment: None, filter: None, over: None, - distinct: false, - special: false, - order_by: vec![], + within_group: vec![], }), expr_from_projection(only(&select.projection)) ); @@ -4266,22 +4276,14 @@ fn parse_named_argument_function_with_eq_operator() { assert_eq!( all_dialects_except(|d| d.supports_named_fn_args_with_eq_operator()) .verified_expr("foo(bar = 42)"), - Expr::Function(Function { - name: ObjectName(vec![Ident::new("foo")]), - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( - Expr::BinaryOp { - left: Box::new(Expr::Identifier(Ident::new("bar"))), - op: BinaryOperator::Eq, - right: Box::new(Expr::Value(number("42"))), - }, - ))], - filter: None, - null_treatment: None, - over: None, - distinct: false, - special: false, - order_by: vec![], - }) + call( + "foo", + [Expr::BinaryOp { + left: Box::new(Expr::Identifier(Ident::new("bar"))), + op: BinaryOperator::Eq, + right: Box::new(Expr::Value(number("42"))), + }] + ), ); // TODO: should this parse for all dialects? @@ -4313,7 +4315,11 @@ fn parse_window_functions() { assert_eq!( &Expr::Function(Function { name: ObjectName(vec![Ident::new("row_number")]), - args: vec![], + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![], + clauses: vec![], + }), null_treatment: None, filter: None, over: Some(WindowType::WindowSpec(WindowSpec { @@ -4326,9 +4332,7 @@ fn parse_window_functions() { }], window_frame: None, })), - distinct: false, - special: false, - order_by: vec![], + within_group: vec![], }), expr_from_projection(&select.projection[0]) ); @@ -4439,21 +4443,23 @@ fn test_parse_named_window() { value: "MIN".to_string(), quote_style: None, }]), - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( - Expr::Identifier(Ident { - value: "c12".to_string(), - quote_style: None, - }), - ))], + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( + Expr::Identifier(Ident { + value: "c12".to_string(), + quote_style: None, + }), + ))], + clauses: vec![], + }), null_treatment: None, filter: None, over: Some(WindowType::NamedWindow(Ident { value: "window1".to_string(), quote_style: None, })), - distinct: false, - special: false, - order_by: vec![], + within_group: vec![], }), alias: Ident { value: "min1".to_string(), @@ -4466,21 +4472,23 @@ fn test_parse_named_window() { value: "MAX".to_string(), quote_style: None, }]), - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( - Expr::Identifier(Ident { - value: "c12".to_string(), - quote_style: None, - }), - ))], + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( + Expr::Identifier(Ident { + value: "c12".to_string(), + quote_style: None, + }), + ))], + clauses: vec![], + }), null_treatment: None, filter: None, over: Some(WindowType::NamedWindow(Ident { value: "window2".to_string(), quote_style: None, })), - distinct: false, - special: false, - order_by: vec![], + within_group: vec![], }), alias: Ident { value: "max1".to_string(), @@ -4982,19 +4990,7 @@ fn parse_at_timezone() { let select = verified_only_select(sql); assert_eq!( &Expr::AtTimeZone { - timestamp: Box::new(Expr::Function(Function { - name: ObjectName(vec![Ident { - value: "FROM_UNIXTIME".to_string(), - quote_style: None, - }]), - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(zero.clone()))], - null_treatment: None, - filter: None, - over: None, - distinct: false, - special: false, - order_by: vec![], - })), + timestamp: Box::new(call("FROM_UNIXTIME", [zero.clone()])), time_zone: "UTC-06:00".to_string(), }, expr_from_projection(only(&select.projection)), @@ -5004,39 +5000,16 @@ fn parse_at_timezone() { let select = verified_only_select(sql); assert_eq!( &SelectItem::ExprWithAlias { - expr: Expr::Function(Function { - name: ObjectName(vec![Ident { - value: "DATE_FORMAT".to_string(), - quote_style: None, - },],), - args: vec![ - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::AtTimeZone { - timestamp: Box::new(Expr::Function(Function { - name: ObjectName(vec![Ident { - value: "FROM_UNIXTIME".to_string(), - quote_style: None, - },],), - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(zero))], - null_treatment: None, - filter: None, - over: None, - distinct: false, - special: false, - order_by: vec![], - },)), + expr: call( + "DATE_FORMAT", + [ + Expr::AtTimeZone { + timestamp: Box::new(call("FROM_UNIXTIME", [zero])), time_zone: "UTC-06:00".to_string(), - },),), - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( - Value::SingleQuotedString("%Y-%m-%dT%H".to_string()), - ),),), - ], - null_treatment: None, - filter: None, - over: None, - distinct: false, - special: false, - order_by: vec![], - },), + }, + Expr::Value(Value::SingleQuotedString("%Y-%m-%dT%H".to_string()),) + ] + ), alias: Ident { value: "hour".to_string(), quote_style: Some('"'), @@ -5185,19 +5158,13 @@ fn parse_table_function() { match only(select.from).relation { TableFactor::TableFunction { expr, alias } => { - let expected_expr = Expr::Function(Function { - name: ObjectName(vec![Ident::new("FUN")]), - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( - Value::SingleQuotedString("1".to_owned()), - )))], - null_treatment: None, - filter: None, - over: None, - distinct: false, - special: false, - order_by: vec![], - }); - assert_eq!(expr, expected_expr); + assert_eq!( + call( + "FUN", + [Expr::Value(Value::SingleQuotedString("1".to_owned()))], + ), + expr + ); assert_eq!(alias, table_alias("a")) } _ => panic!("Expecting TableFactor::TableFunction"), @@ -5336,20 +5303,14 @@ fn parse_unnest_in_from_clause() { vec![TableWithJoins { relation: TableFactor::UNNEST { alias: None, - array_exprs: vec![Expr::Function(Function { - name: ObjectName(vec![Ident::new("make_array")]), - args: vec![ - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(number("1")))), - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(number("2")))), - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(number("3")))), + array_exprs: vec![call( + "make_array", + [ + Expr::Value(number("1")), + Expr::Value(number("2")), + Expr::Value(number("3")), ], - null_treatment: None, - filter: None, - over: None, - distinct: false, - special: false, - order_by: vec![], - })], + )], with_offset: false, with_offset_alias: None, }, @@ -5367,33 +5328,18 @@ fn parse_unnest_in_from_clause() { relation: TableFactor::UNNEST { alias: None, array_exprs: vec![ - Expr::Function(Function { - name: ObjectName(vec![Ident::new("make_array")]), - args: vec![ - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(number("1")))), - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(number("2")))), - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(number("3")))), + call( + "make_array", + [ + Expr::Value(number("1")), + Expr::Value(number("2")), + Expr::Value(number("3")), ], - null_treatment: None, - filter: None, - over: None, - distinct: false, - special: false, - order_by: vec![], - }), - Expr::Function(Function { - name: ObjectName(vec![Ident::new("make_array")]), - args: vec![ - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(number("5")))), - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(number("6")))), - ], - null_treatment: None, - filter: None, - over: None, - distinct: false, - special: false, - order_by: vec![], - }), + ), + call( + "make_array", + [Expr::Value(number("5")), Expr::Value(number("6"))], + ), ], with_offset: false, with_offset_alias: None, @@ -8001,13 +7947,15 @@ fn parse_time_functions() { let select = verified_only_select(&sql); let select_localtime_func_call_ast = Function { name: ObjectName(vec![Ident::new(func_name)]), - args: vec![], + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![], + clauses: vec![], + }), null_treatment: None, filter: None, over: None, - distinct: false, - special: false, - order_by: vec![], + within_group: vec![], }; assert_eq!( &Expr::Function(select_localtime_func_call_ast.clone()), @@ -8017,7 +7965,7 @@ fn parse_time_functions() { // Validating Parenthesis let sql_without_parens = format!("SELECT {}", func_name); let mut ast_without_parens = select_localtime_func_call_ast; - ast_without_parens.special = true; + ast_without_parens.args = FunctionArguments::None; assert_eq!( &Expr::Function(ast_without_parens), expr_from_projection(&verified_only_select(&sql_without_parens).projection[0]) @@ -8556,18 +8504,13 @@ fn parse_pivot_table() { fn expected_function(table: &'static str, alias: Option<&'static str>) -> ExprWithAlias { ExprWithAlias { - expr: Expr::Function(Function { - name: ObjectName(vec![Ident::new("SUM")]), - args: (vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( - Expr::CompoundIdentifier(vec![Ident::new(table), Ident::new("amount")]), - ))]), - null_treatment: None, - filter: None, - over: None, - distinct: false, - special: false, - order_by: vec![], - }), + expr: call( + "SUM", + [Expr::CompoundIdentifier(vec![ + Ident::new(table), + Ident::new("amount"), + ])], + ), alias: alias.map(Ident::new), } } @@ -8739,18 +8682,7 @@ fn parse_pivot_unpivot_table() { }), }), aggregate_functions: vec![ExprWithAlias { - expr: Expr::Function(Function { - name: ObjectName(vec![Ident::new("sum")]), - args: (vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( - Expr::Identifier(Ident::new("population")) - ))]), - null_treatment: None, - filter: None, - over: None, - distinct: false, - special: false, - order_by: vec![], - }), + expr: call("sum", [Expr::Identifier(Ident::new("population"))]), alias: None }], value_column: vec![Ident::new("year")], @@ -8876,16 +8808,18 @@ fn parse_call() { assert_eq!( verified_stmt("CALL my_procedure('a')"), Statement::Call(Function { - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( - Value::SingleQuotedString("a".to_string()) - ))),], + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( + Value::SingleQuotedString("a".to_string()) + )))], + clauses: vec![], + }), name: ObjectName(vec![Ident::new("my_procedure")]), filter: None, null_treatment: None, over: None, - distinct: false, - special: false, - order_by: vec![] + within_group: vec![], }) ); } @@ -9085,18 +9019,7 @@ fn parse_map_access_expr() { syntax: MapAccessSyntax::Bracket, }, MapAccessKey { - key: Expr::Function(Function { - name: ObjectName(vec![Ident::new("safe_offset")]), - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value( - number("2"), - )))], - filter: None, - null_treatment: None, - over: None, - distinct: false, - special: false, - order_by: vec![], - }), + key: call("safe_offset", [Expr::Value(number("2"))]), syntax: MapAccessSyntax::Bracket, }, ], @@ -9284,34 +9207,42 @@ fn test_selective_aggregation() { .verified_only_select(sql) .projection, vec![ - SelectItem::UnnamedExpr(Expr::AggregateExpressionWithFilter { - expr: Box::new(Expr::ArrayAgg(ArrayAgg { - distinct: false, - expr: Box::new(Expr::Identifier(Ident::new("name"))), - order_by: None, - limit: None, - within_group: false, - })), - filter: Box::new(Expr::IsNotNull(Box::new(Expr::Identifier(Ident::new( - "name" + SelectItem::UnnamedExpr(Expr::Function(Function { + name: ObjectName(vec![Ident::new("ARRAY_AGG")]), + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( + Expr::Identifier(Ident::new("name")) + ))], + clauses: vec![], + }), + filter: Some(Box::new(Expr::IsNotNull(Box::new(Expr::Identifier( + Ident::new("name") ))))), - }), + over: None, + within_group: vec![], + null_treatment: None + })), SelectItem::ExprWithAlias { - expr: Expr::AggregateExpressionWithFilter { - expr: Box::new(Expr::ArrayAgg(ArrayAgg { - distinct: false, - expr: Box::new(Expr::Identifier(Ident::new("name"))), - order_by: None, - limit: None, - within_group: false, - })), - filter: Box::new(Expr::Like { + expr: Expr::Function(Function { + name: ObjectName(vec![Ident::new("ARRAY_AGG")]), + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( + Expr::Identifier(Ident::new("name")) + ))], + clauses: vec![], + }), + filter: Some(Box::new(Expr::Like { negated: false, expr: Box::new(Expr::Identifier(Ident::new("name"))), pattern: Box::new(Expr::Value(Value::SingleQuotedString("a%".to_owned()))), escape_char: None, - }), - }, + })), + null_treatment: None, + over: None, + within_group: vec![] + }), alias: Ident::new("agg2") }, ] @@ -9652,18 +9583,7 @@ fn test_select_wildcard_with_replace() { let expected = SelectItem::Wildcard(WildcardAdditionalOptions { opt_replace: Some(ReplaceSelectItem { items: vec![Box::new(ReplaceSelectElement { - expr: Expr::Function(Function { - name: ObjectName(vec![Ident::new("lower")]), - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( - Expr::Identifier(Ident::new("city")), - ))], - filter: None, - null_treatment: None, - over: None, - distinct: false, - special: false, - order_by: vec![], - }), + expr: call("lower", [Expr::Identifier(Ident::new("city"))]), column_name: Ident::new("city"), as_keyword: true, })], @@ -9799,3 +9719,12 @@ fn test_dictionary_syntax() { ]), ) } + +#[test] +fn parse_within_group() { + verified_expr("PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY sales_amount)"); + verified_expr(concat!( + "PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY sales_amount) ", + "OVER (PARTITION BY department)", + )); +} diff --git a/tests/sqlparser_duckdb.rs b/tests/sqlparser_duckdb.rs index d49a5d548..37fbbfbfc 100644 --- a/tests/sqlparser_duckdb.rs +++ b/tests/sqlparser_duckdb.rs @@ -488,28 +488,30 @@ fn test_duckdb_named_argument_function_with_assignment_operator() { assert_eq!( &Expr::Function(Function { name: ObjectName(vec![Ident::new("FUN")]), - args: vec![ - FunctionArg::Named { - name: Ident::new("a"), - arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString( - "1".to_owned() - ))), - operator: FunctionArgOperator::Assignment - }, - FunctionArg::Named { - name: Ident::new("b"), - arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString( - "2".to_owned() - ))), - operator: FunctionArgOperator::Assignment - }, - ], + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![ + FunctionArg::Named { + name: Ident::new("a"), + arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString( + "1".to_owned() + ))), + operator: FunctionArgOperator::Assignment + }, + FunctionArg::Named { + name: Ident::new("b"), + arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString( + "2".to_owned() + ))), + operator: FunctionArgOperator::Assignment + }, + ], + clauses: vec![], + }), null_treatment: None, filter: None, over: None, - distinct: false, - special: false, - order_by: vec![], + within_group: vec![], }), expr_from_projection(only(&select.projection)) ); diff --git a/tests/sqlparser_hive.rs b/tests/sqlparser_hive.rs index 3157233f4..acd45005b 100644 --- a/tests/sqlparser_hive.rs +++ b/tests/sqlparser_hive.rs @@ -16,8 +16,9 @@ //! is also tested (on the inputs it can handle). use sqlparser::ast::{ - CreateFunctionBody, CreateFunctionUsing, Expr, Function, FunctionDefinition, Ident, ObjectName, - SelectItem, Statement, TableFactor, UnaryOperator, + CreateFunctionBody, CreateFunctionUsing, Expr, Function, FunctionArgumentList, + FunctionArguments, FunctionDefinition, Ident, ObjectName, SelectItem, Statement, TableFactor, + UnaryOperator, }; use sqlparser::dialect::{GenericDialect, HiveDialect, MsSqlDialect}; use sqlparser::parser::{ParserError, ParserOptions}; @@ -379,13 +380,15 @@ fn parse_delimited_identifiers() { assert_eq!( &Expr::Function(Function { name: ObjectName(vec![Ident::with_quote('"', "myfun")]), - args: vec![], + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![], + clauses: vec![], + }), null_treatment: None, filter: None, over: None, - distinct: false, - special: false, - order_by: vec![], + within_group: vec![], }), expr_from_projection(&select.projection[1]), ); diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index 6017c978f..5d61c6ab9 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -354,13 +354,15 @@ fn parse_delimited_identifiers() { assert_eq!( &Expr::Function(Function { name: ObjectName(vec![Ident::with_quote('"', "myfun")]), - args: vec![], + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![], + clauses: vec![], + }), null_treatment: None, filter: None, over: None, - distinct: false, - special: false, - order_by: vec![], + within_group: vec![], }), expr_from_projection(&select.projection[1]), ); diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index 60516213e..6a71a44de 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -1634,78 +1634,23 @@ fn parse_insert_with_on_duplicate_update() { Some(OnInsert::DuplicateKeyUpdate(vec![ Assignment { id: vec![Ident::new("description".to_string())], - value: Expr::Function(Function { - name: ObjectName(vec![Ident::new("VALUES".to_string()),]), - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( - Expr::Identifier(Ident::new("description")) - ))], - null_treatment: None, - filter: None, - over: None, - distinct: false, - special: false, - order_by: vec![], - }) + value: call("VALUES", [Expr::Identifier(Ident::new("description"))]), }, Assignment { id: vec![Ident::new("perm_create".to_string())], - value: Expr::Function(Function { - name: ObjectName(vec![Ident::new("VALUES".to_string()),]), - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( - Expr::Identifier(Ident::new("perm_create")) - ))], - null_treatment: None, - filter: None, - over: None, - distinct: false, - special: false, - order_by: vec![], - }) + value: call("VALUES", [Expr::Identifier(Ident::new("perm_create"))]), }, Assignment { id: vec![Ident::new("perm_read".to_string())], - value: Expr::Function(Function { - name: ObjectName(vec![Ident::new("VALUES".to_string()),]), - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( - Expr::Identifier(Ident::new("perm_read")) - ))], - null_treatment: None, - filter: None, - over: None, - distinct: false, - special: false, - order_by: vec![], - }) + value: call("VALUES", [Expr::Identifier(Ident::new("perm_read"))]), }, Assignment { id: vec![Ident::new("perm_update".to_string())], - value: Expr::Function(Function { - name: ObjectName(vec![Ident::new("VALUES".to_string()),]), - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( - Expr::Identifier(Ident::new("perm_update")) - ))], - null_treatment: None, - filter: None, - over: None, - distinct: false, - special: false, - order_by: vec![], - }) + value: call("VALUES", [Expr::Identifier(Ident::new("perm_update"))]), }, Assignment { id: vec![Ident::new("perm_delete".to_string())], - value: Expr::Function(Function { - name: ObjectName(vec![Ident::new("VALUES".to_string()),]), - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( - Expr::Identifier(Ident::new("perm_delete")) - ))], - null_treatment: None, - filter: None, - over: None, - distinct: false, - special: false, - order_by: vec![], - }) + value: call("VALUES", [Expr::Identifier(Ident::new("perm_delete"))]), }, ])), on @@ -2385,16 +2330,7 @@ fn parse_table_colum_option_on_update() { collation: None, options: vec![ColumnOptionDef { name: None, - option: ColumnOption::OnUpdate(Expr::Function(Function { - name: ObjectName(vec![Ident::new("CURRENT_TIMESTAMP")]), - args: vec![], - null_treatment: None, - filter: None, - over: None, - distinct: false, - special: false, - order_by: vec![], - })), + option: ColumnOption::OnUpdate(call("CURRENT_TIMESTAMP", [])), },], }], columns diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 8c258586d..5701f6b2b 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -2151,58 +2151,65 @@ fn parse_array_subquery_expr() { let sql = "SELECT ARRAY(SELECT 1 UNION SELECT 2)"; let select = pg().verified_only_select(sql); assert_eq!( - &Expr::ArraySubquery(Box::new(Query { - with: None, - body: Box::new(SetExpr::SetOperation { - op: SetOperator::Union, - set_quantifier: SetQuantifier::None, - left: Box::new(SetExpr::Select(Box::new(Select { - distinct: None, - top: None, - projection: vec![SelectItem::UnnamedExpr(Expr::Value(number("1")))], - into: None, - from: vec![], - lateral_views: vec![], - selection: None, - group_by: GroupByExpr::Expressions(vec![]), - cluster_by: vec![], - distribute_by: vec![], - sort_by: vec![], - having: None, - named_window: vec![], - qualify: None, - window_before_qualify: false, - value_table_mode: None, - connect_by: None, - }))), - right: Box::new(SetExpr::Select(Box::new(Select { - distinct: None, - top: None, - projection: vec![SelectItem::UnnamedExpr(Expr::Value(number("2")))], - into: None, - from: vec![], - lateral_views: vec![], - selection: None, - group_by: GroupByExpr::Expressions(vec![]), - cluster_by: vec![], - distribute_by: vec![], - sort_by: vec![], - having: None, - named_window: vec![], - qualify: None, - window_before_qualify: false, - value_table_mode: None, - connect_by: None, - }))), - }), - order_by: vec![], - limit: None, - limit_by: vec![], - offset: None, - fetch: None, - locks: vec![], - for_clause: None, - })), + &Expr::Function(Function { + name: ObjectName(vec![Ident::new("ARRAY")]), + args: FunctionArguments::Subquery(Box::new(Query { + with: None, + body: Box::new(SetExpr::SetOperation { + op: SetOperator::Union, + set_quantifier: SetQuantifier::None, + left: Box::new(SetExpr::Select(Box::new(Select { + distinct: None, + top: None, + projection: vec![SelectItem::UnnamedExpr(Expr::Value(number("1")))], + into: None, + from: vec![], + lateral_views: vec![], + selection: None, + group_by: GroupByExpr::Expressions(vec![]), + cluster_by: vec![], + distribute_by: vec![], + sort_by: vec![], + having: None, + named_window: vec![], + qualify: None, + window_before_qualify: false, + value_table_mode: None, + connect_by: None, + }))), + right: Box::new(SetExpr::Select(Box::new(Select { + distinct: None, + top: None, + projection: vec![SelectItem::UnnamedExpr(Expr::Value(number("2")))], + into: None, + from: vec![], + lateral_views: vec![], + selection: None, + group_by: GroupByExpr::Expressions(vec![]), + cluster_by: vec![], + distribute_by: vec![], + sort_by: vec![], + having: None, + named_window: vec![], + qualify: None, + window_before_qualify: false, + value_table_mode: None, + connect_by: None, + }))), + }), + order_by: vec![], + limit: None, + limit_by: vec![], + offset: None, + fetch: None, + locks: vec![], + for_clause: None, + })), + filter: None, + null_treatment: None, + over: None, + within_group: vec![] + }), expr_from_projection(only(&select.projection)), ); } @@ -2506,21 +2513,23 @@ fn test_composite_value() { Ident::new("information_schema"), Ident::new("_pg_expandarray") ]), - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Array( - Array { - elem: vec![ - Expr::Value(Value::SingleQuotedString("i".to_string())), - Expr::Value(Value::SingleQuotedString("i".to_string())), - ], - named: true - } - )))], + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Array( + Array { + elem: vec![ + Expr::Value(Value::SingleQuotedString("i".to_string())), + Expr::Value(Value::SingleQuotedString("i".to_string())), + ], + named: true + } + )))], + clauses: vec![], + }), null_treatment: None, filter: None, over: None, - distinct: false, - special: false, - order_by: vec![], + within_group: vec![], })))) }), select.projection[0] @@ -2730,52 +2739,44 @@ fn parse_current_functions() { assert_eq!( &Expr::Function(Function { name: ObjectName(vec![Ident::new("CURRENT_CATALOG")]), - args: vec![], + args: FunctionArguments::None, null_treatment: None, filter: None, over: None, - distinct: false, - special: true, - order_by: vec![], + within_group: vec![], }), expr_from_projection(&select.projection[0]) ); assert_eq!( &Expr::Function(Function { name: ObjectName(vec![Ident::new("CURRENT_USER")]), - args: vec![], + args: FunctionArguments::None, null_treatment: None, filter: None, over: None, - distinct: false, - special: true, - order_by: vec![], + within_group: vec![], }), expr_from_projection(&select.projection[1]) ); assert_eq!( &Expr::Function(Function { name: ObjectName(vec![Ident::new("SESSION_USER")]), - args: vec![], + args: FunctionArguments::None, null_treatment: None, filter: None, over: None, - distinct: false, - special: true, - order_by: vec![], + within_group: vec![], }), expr_from_projection(&select.projection[2]) ); assert_eq!( &Expr::Function(Function { name: ObjectName(vec![Ident::new("USER")]), - args: vec![], + args: FunctionArguments::None, null_treatment: None, filter: None, over: None, - distinct: false, - special: true, - order_by: vec![], + within_group: vec![], }), expr_from_projection(&select.projection[3]) ); @@ -3221,13 +3222,15 @@ fn parse_delimited_identifiers() { assert_eq!( &Expr::Function(Function { name: ObjectName(vec![Ident::with_quote('"', "myfun")]), - args: vec![], + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![], + clauses: vec![], + }), null_treatment: None, filter: None, over: None, - distinct: false, - special: false, - order_by: vec![], + within_group: vec![], }), expr_from_projection(&select.projection[1]), ); diff --git a/tests/sqlparser_redshift.rs b/tests/sqlparser_redshift.rs index 3de229676..0a5710ff4 100644 --- a/tests/sqlparser_redshift.rs +++ b/tests/sqlparser_redshift.rs @@ -136,13 +136,15 @@ fn parse_delimited_identifiers() { assert_eq!( &Expr::Function(Function { name: ObjectName(vec![Ident::with_quote('"', "myfun")]), - args: vec![], + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![], + clauses: vec![], + }), null_treatment: None, filter: None, over: None, - distinct: false, - special: false, - order_by: vec![], + within_group: vec![], }), expr_from_projection(&select.projection[1]), ); diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index 59630814a..25eaa2f71 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -382,13 +382,15 @@ fn parse_delimited_identifiers() { assert_eq!( &Expr::Function(Function { name: ObjectName(vec![Ident::with_quote('"', "myfun")]), - args: vec![], + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![], + clauses: vec![], + }), filter: None, null_treatment: None, over: None, - distinct: false, - special: false, - order_by: vec![], + within_group: vec![], }), expr_from_projection(&select.projection[1]), ); @@ -413,15 +415,6 @@ fn test_array_agg_func() { ] { snowflake().verified_stmt(sql); } - - let sql = "select array_agg(x order by x) as a from T"; - let result = snowflake().parse_sql_statements(sql); - assert_eq!( - result, - Err(ParserError::ParserError(String::from( - "Expected ), found: order" - ))) - ) } fn snowflake() -> TestedDialects { @@ -1429,20 +1422,15 @@ fn parse_position_not_function_columns() { fn parse_subquery_function_argument() { // Snowflake allows passing an unparenthesized subquery as the single // argument to a function. - snowflake().one_statement_parses_to( - "SELECT parse_json(SELECT '{}')", - "SELECT parse_json((SELECT '{}'))", - ); + snowflake().verified_stmt("SELECT parse_json(SELECT '{}')"); // Subqueries that begin with WITH work too. - snowflake().one_statement_parses_to( - "SELECT parse_json(WITH q AS (SELECT '{}' AS foo) SELECT foo FROM q)", - "SELECT parse_json((WITH q AS (SELECT '{}' AS foo) SELECT foo FROM q))", - ); + snowflake() + .verified_stmt("SELECT parse_json(WITH q AS (SELECT '{}' AS foo) SELECT foo FROM q)"); // Commas are parsed as part of the subquery, not additional arguments to // the function. - snowflake().one_statement_parses_to("SELECT func(SELECT 1, 2)", "SELECT func((SELECT 1, 2))"); + snowflake().verified_stmt("SELECT func(SELECT 1, 2)"); } #[test] @@ -1528,19 +1516,13 @@ fn parse_comma_outer_join() { Some(Expr::BinaryOp { left: Box::new(Expr::Identifier(Ident::new("c1"))), op: BinaryOperator::Eq, - right: Box::new(Expr::Function(Function { - name: ObjectName(vec![Ident::new("myudf")]), - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::UnaryOp { + right: Box::new(call( + "myudf", + [Expr::UnaryOp { op: UnaryOperator::Plus, expr: Box::new(Expr::Value(number("42"))) - }))], - filter: None, - null_treatment: None, - over: None, - distinct: false, - special: false, - order_by: vec![] - })) + }] + )), }) ); diff --git a/tests/sqlparser_sqlite.rs b/tests/sqlparser_sqlite.rs index b90e45827..5742754c0 100644 --- a/tests/sqlparser_sqlite.rs +++ b/tests/sqlparser_sqlite.rs @@ -332,9 +332,13 @@ fn parse_window_function_with_filter() { select.projection, vec![SelectItem::UnnamedExpr(Expr::Function(Function { name: ObjectName(vec![Ident::new(func_name)]), - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( - Expr::Identifier(Ident::new("x")) - ))], + args: FunctionArguments::List(FunctionArgumentList { + duplicate_treatment: None, + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( + Expr::Identifier(Ident::new("x")) + ))], + clauses: vec![], + }), null_treatment: None, over: Some(WindowType::WindowSpec(WindowSpec { window_name: None, @@ -343,9 +347,7 @@ fn parse_window_function_with_filter() { window_frame: None, })), filter: Some(Box::new(Expr::Identifier(Ident::new("y")))), - distinct: false, - special: false, - order_by: vec![] + within_group: vec![], }))] ); }