mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-07-07 17:04:59 +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![],
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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))");
|
||||
}
|
||||
|
|
|
@ -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]),
|
||||
);
|
||||
|
|
|
@ -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)",
|
||||
));
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
);
|
||||
|
|
|
@ -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]),
|
||||
);
|
||||
|
|
|
@ -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]),
|
||||
);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]),
|
||||
);
|
||||
|
|
|
@ -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]),
|
||||
);
|
||||
|
|
|
@ -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![]
|
||||
}))
|
||||
}]
|
||||
)),
|
||||
})
|
||||
);
|
||||
|
||||
|
|
|
@ -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![],
|
||||
}))]
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue