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![],
})
}

View file

@ -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))");
}

View file

@ -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]),
);

View file

@ -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)",
));
}

View file

@ -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))
);

View file

@ -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]),
);

View file

@ -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]),
);

View file

@ -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

View file

@ -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]),
);

View file

@ -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]),
);

View file

@ -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![]
}))
}]
)),
})
);

View file

@ -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![],
}))]
);
}