Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5333,6 +5333,8 @@ pub enum FunctionArgOperator {
RightArrow,
/// function(arg1 := value1)
Assignment,
/// function(arg1 : value1)
Colon,
}

impl fmt::Display for FunctionArgOperator {
Expand All @@ -5341,6 +5343,7 @@ impl fmt::Display for FunctionArgOperator {
FunctionArgOperator::Equals => f.write_str("="),
FunctionArgOperator::RightArrow => f.write_str("=>"),
FunctionArgOperator::Assignment => f.write_str(":="),
FunctionArgOperator::Colon => f.write_str(":"),
}
}
}
Expand All @@ -5350,7 +5353,7 @@ impl fmt::Display for FunctionArgOperator {
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FunctionArg {
Named {
name: Ident,
name: Expr,
arg: FunctionArgExpr,
operator: FunctionArgOperator,
},
Expand Down Expand Up @@ -5503,7 +5506,10 @@ impl fmt::Display for FunctionArgumentList {
}
write!(f, "{}", display_comma_separated(&self.args))?;
if !self.clauses.is_empty() {
write!(f, " {}", display_separated(&self.clauses, " "))?;
if !self.args.is_empty() {
write!(f, " ")?;
}
write!(f, "{}", display_separated(&self.clauses, " "))?;
}
Ok(())
}
Expand Down Expand Up @@ -5545,6 +5551,11 @@ pub enum FunctionArgumentClause {
///
/// [`GROUP_CONCAT`]: https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html#function_group-concat
Separator(Value),
/// The json-null-clause to the [`JSON_ARRAY`]/[`JSON_OBJECT`] function in MSSQL.
///
/// [`JSON_ARRAY`]: <https://learn.microsoft.com/en-us/sql/t-sql/functions/json-array-transact-sql?view=sql-server-ver16>
/// [`JSON_OBJECT`]: <https://learn.microsoft.com/en-us/sql/t-sql/functions/json-object-transact-sql?view=sql-server-ver16>
JsonNullClause(JsonNullClause),
}

impl fmt::Display for FunctionArgumentClause {
Expand All @@ -5560,6 +5571,7 @@ impl fmt::Display for FunctionArgumentClause {
FunctionArgumentClause::OnOverflow(on_overflow) => write!(f, "{on_overflow}"),
FunctionArgumentClause::Having(bound) => write!(f, "{bound}"),
FunctionArgumentClause::Separator(sep) => write!(f, "SEPARATOR {sep}"),
FunctionArgumentClause::JsonNullClause(null_clause) => write!(f, "{null_clause}"),
}
}
}
Expand Down Expand Up @@ -7317,6 +7329,24 @@ impl Display for UtilityOption {
}
}

/// MSSQL's json null clause
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum JsonNullClause {
NullOnNull,
AbsentOnNull,
}

impl Display for JsonNullClause {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
JsonNullClause::NullOnNull => write!(f, "NULL ON NULL"),
JsonNullClause::AbsentOnNull => write!(f, "ABSENT ON NULL"),
}
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
1 change: 1 addition & 0 deletions src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ macro_rules! define_keywords {
define_keywords!(
ABORT,
ABS,
ABSENT,
ABSOLUTE,
ACCESS,
ACTION,
Expand Down
55 changes: 49 additions & 6 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11060,7 +11060,7 @@ impl<'a> Parser<'a> {

pub fn parse_function_args(&mut self) -> Result<FunctionArg, ParserError> {
if self.peek_nth_token(1) == Token::RArrow {
let name = self.parse_identifier(false)?;
let name = Expr::Identifier(self.parse_identifier(false)?);

self.expect_token(&Token::RArrow)?;
let arg = self.parse_wildcard_expr()?.into();
Expand All @@ -11073,7 +11073,7 @@ impl<'a> Parser<'a> {
} else if self.dialect.supports_named_fn_args_with_eq_operator()
&& self.peek_nth_token(1) == Token::Eq
{
let name = self.parse_identifier(false)?;
let name = Expr::Identifier(self.parse_identifier(false)?);

self.expect_token(&Token::Eq)?;
let arg = self.parse_wildcard_expr()?.into();
Expand All @@ -11086,7 +11086,7 @@ impl<'a> Parser<'a> {
} else if dialect_of!(self is DuckDbDialect | GenericDialect)
&& self.peek_nth_token(1) == Token::Assignment
{
let name = self.parse_identifier(false)?;
let name = Expr::Identifier(self.parse_identifier(false)?);

self.expect_token(&Token::Assignment)?;
let arg = self.parse_expr()?.into();
Expand All @@ -11096,6 +11096,19 @@ impl<'a> Parser<'a> {
arg,
operator: FunctionArgOperator::Assignment,
})
} else if dialect_of!(self is MsSqlDialect) {
// FUNC(<expr> : <expr>)
let name = self.parse_wildcard_expr()?;
if self.consume_token(&Token::Colon) {
let arg = self.parse_expr()?.into();
Ok(FunctionArg::Named {
name,
arg,
operator: FunctionArgOperator::Colon,
})
} else {
Ok(FunctionArg::Unnamed(name.into()))
}
} else {
Ok(FunctionArg::Unnamed(self.parse_wildcard_expr()?.into()))
}
Expand Down Expand Up @@ -11141,19 +11154,34 @@ impl<'a> Parser<'a> {
/// FIRST_VALUE(x IGNORE NULL);
/// ```
fn parse_function_argument_list(&mut self) -> Result<FunctionArgumentList, ParserError> {
let mut clauses = vec![];

if dialect_of!(self is MsSqlDialect) {
// JSON_ARRAY(NULL ON NULL)
if self.parse_keywords(&[Keyword::NULL, Keyword::ON, Keyword::NULL]) {
clauses.push(FunctionArgumentClause::JsonNullClause(
JsonNullClause::NullOnNull,
));
}
// JSON_ARRAY(ABSENT ON NULL)
else if self.parse_keywords(&[Keyword::ABSENT, Keyword::ON, Keyword::NULL]) {
clauses.push(FunctionArgumentClause::JsonNullClause(
JsonNullClause::AbsentOnNull,
));
}
}

if self.consume_token(&Token::RParen) {
return Ok(FunctionArgumentList {
duplicate_treatment: None,
args: vec![],
clauses: vec![],
clauses,
});
}

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));
Expand Down Expand Up @@ -11194,6 +11222,21 @@ impl<'a> Parser<'a> {
clauses.push(FunctionArgumentClause::OnOverflow(on_overflow));
}

if dialect_of!(self is MsSqlDialect) {
// JSON_ARRAY(<arg-list> NULL ON NULL)
if self.parse_keywords(&[Keyword::NULL, Keyword::ON, Keyword::NULL]) {
clauses.push(FunctionArgumentClause::JsonNullClause(
JsonNullClause::NullOnNull,
));
}
// JSON_ARRAY(<keyvalue-pair-list> ABSENT ON NULL)
else if self.parse_keywords(&[Keyword::ABSENT, Keyword::ON, Keyword::NULL]) {
clauses.push(FunctionArgumentClause::JsonNullClause(
JsonNullClause::AbsentOnNull,
));
}
}

self.expect_token(&Token::RParen)?;
Ok(FunctionArgumentList {
duplicate_treatment,
Expand Down
8 changes: 4 additions & 4 deletions tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4421,14 +4421,14 @@ fn parse_named_argument_function() {
duplicate_treatment: None,
args: vec![
FunctionArg::Named {
name: Ident::new("a"),
name: Expr::Identifier(Ident::new("a")),
arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString(
"1".to_owned()
))),
operator: FunctionArgOperator::RightArrow
},
FunctionArg::Named {
name: Ident::new("b"),
name: Expr::Identifier(Ident::new("b")),
arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString(
"2".to_owned()
))),
Expand Down Expand Up @@ -4460,14 +4460,14 @@ fn parse_named_argument_function_with_eq_operator() {
duplicate_treatment: None,
args: vec![
FunctionArg::Named {
name: Ident::new("a"),
name: Expr::Identifier(Ident::new("a")),
arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString(
"1".to_owned()
))),
operator: FunctionArgOperator::Equals
},
FunctionArg::Named {
name: Ident::new("b"),
name: Expr::Identifier(Ident::new("b")),
arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString(
"2".to_owned()
))),
Expand Down
4 changes: 2 additions & 2 deletions tests/sqlparser_duckdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -611,14 +611,14 @@ fn test_duckdb_named_argument_function_with_assignment_operator() {
duplicate_treatment: None,
args: vec![
FunctionArg::Named {
name: Ident::new("a"),
name: Expr::Identifier(Ident::new("a")),
arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString(
"1".to_owned()
))),
operator: FunctionArgOperator::Assignment
},
FunctionArg::Named {
name: Ident::new("b"),
name: Expr::Identifier(Ident::new("b")),
arg: FunctionArgExpr::Expr(Expr::Value(Value::SingleQuotedString(
"2".to_owned()
))),
Expand Down
Loading