mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-11-01 23:50:56 +00:00
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:
parent
5ea9c01bb2
commit
a14faa36bb
16 changed files with 773 additions and 1040 deletions
346
src/ast/mod.rs
346
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<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))]
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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![],
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue